Initial commit of etch c-binding.
The main parts are
- etch c-binding runtime
- etch c-binding compiler
- etch c-binding examples
- changes to etch config files to include also the c-binding
git-svn-id: https://svn.apache.org/repos/asf/incubator/etch/branches/etch-c@966314 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/NOTICE.txt b/NOTICE.txt
index ad3daf3..9fac40f 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -2,6 +2,7 @@
Copyright (C) 2008-2009 The Apache Software Foundation
Copyright (C) 2007-2008 Cisco Systems Inc.
+Copyright (C) 2009-2010 BMW Car IT GmbH
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
diff --git a/binding-c/build.xml b/binding-c/build.xml
new file mode 100644
index 0000000..5b24337
--- /dev/null
+++ b/binding-c/build.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8" ?>
+ <!--
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ -->
+<project name="etch-c-binding" basedir="." default="help">
+ <description>Etch-to-C Binding</description>
+ <property name="Etch.basedir" location="${basedir}/.." />
+ <import file="${Etch.basedir}/build-support/etch.common.xml" />
+
+ <!-- standard, supported targets -->
+ <target name="Debug" depends="debug" />
+ <target name="Release" depends="release" />
+ <target name="Clean" depends="clean" />
+ <target name="debug" depends="init-debug,component-all,post-debug" />
+ <target name="release" depends="init-release,component-all,post-release" />
+ <target name="clean" depends="init-clean,component-all,post-clean" />
+ <target name="maven-install" depends="init-maven,release" />
+
+ <target name="init-maven" >
+ <property name="DO.maven.install" value="true" />
+ </target>
+
+ <target name="validate-dependencies" >
+ <mkdir dir="${Etch.logDirectory}" />
+ </target>
+
+ <target name="init-debug" depends="validate-dependencies" >
+ <property name="Etch.build.target" value="Debug" />
+ <property name="Etch.javac.debug" value="on" />
+ <property name="Etch.javac.optimize" value="off" />
+ </target>
+
+ <target name="post-debug" >
+ </target>
+
+ <target name="init-release" depends="validate-dependencies">
+ <!-- For now, keep debug-symbols and no-optimize, even for release builds -->
+ <property name="Etch.build.target" value="Release" />
+ <property name="Etch.javac.debug" value="on" />
+ <property name="Etch.javac.optimize" value="off" />
+ <!--
+ <property name="Etch.javac.debug" value="off" />
+ <property name="Etch.javac.optimize" value="on" />
+ -->
+ </target>
+
+ <target name="post-release" >
+ </target>
+
+ <target name="init-clean" depends="validate-dependencies">
+ <property name="Etch.build.target" value="Clean" />
+ </target>
+
+ <target name="post-clean" >
+ <echo message="Executing clean" />
+ </target>
+
+ <target name="component-all" >
+ <build_component dir="compiler" />
+ <!--<build_component dir="runtime" />-->
+ </target>
+
+</project>
diff --git a/binding-c/compiler/build.xml b/binding-c/compiler/build.xml
new file mode 100644
index 0000000..20a77f8
--- /dev/null
+++ b/binding-c/compiler/build.xml
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="utf-8" ?>
+ <!--
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ -->
+<project name="etch-c-compiler" basedir="." default="help">
+ <description>Etch-to-C compiler implementation</description>
+ <property name="Etch.basedir" location="${basedir}/../.." />
+ <import file="${Etch.basedir}/build-support/etch.includes.xml" />
+
+ <!-- Static properties of the sub-project -->
+ <property name="proj" location="${Etch.basedir}/binding-c/compiler" />
+ <property name="target" location="${proj}/target" />
+ <property name="src" location="${proj}/src" />
+ <property name="generatedSrc" location="${target}/generated-sources" />
+ <property name="classesDirectory" location="${target}/classes" />
+ <property name="resourcesDirectory" location="${target}/resources" />
+ <property name="testResultsDirectory" location="${target}/test-results" />
+
+ <!-- MACRO: init-target -->
+ <macrodef name="init-target" >
+ <sequential>
+ <delete dir="${classesDirectory}" failonerror="false" quiet="true" />
+ <delete dir="${resourcesDirectory}" failonerror="false" quiet="true" />
+ <mkdir dir="${classesDirectory}" />
+ <mkdir dir="${classesDirectory}/main" />
+ <mkdir dir="${classesDirectory}/test" />
+ <mkdir dir="${resourcesDirectory}" />
+ </sequential>
+ </macrodef>
+
+ <!-- MACRO: compile-sources -->
+ <macrodef name="compile-sources" >
+ <sequential>
+ <!-- compile compiler plugin -->
+ <javac debug="${Etch.javac.debug}"
+ target="1.5"
+ optimize="${Etch.javac.optimize}"
+ destdir="${classesDirectory}/main" >
+ <src path="${src}/main/java" />
+ <exclude name="**/.svn/**" />
+ <classpath refid="Etch.dependencies.jar.paths" />
+ <classpath>
+ <pathelement location="${Etch.HOME}/lib/${etch-compiler.jar}" />
+ </classpath>
+ </javac>
+
+ <!-- compiler plugin resources -->
+ <copy todir="${classesDirectory}/main" >
+ <fileset dir="${src}/main/resources">
+ <include name="**/*.kwd" />
+ <include name="**/*.vm" />
+ </fileset>
+ </copy>
+
+ <!--
+ <javac debug="${Etch.javac.debug}"
+ target="1.5"
+ optimize="${Etch.javac.optimize}"
+ destdir="${classesDirectory}/test" >
+ <src path="${src}/test/java" />
+ <exclude name="**/.svn/**" />
+ <classpath refid="Etch.dependencies.jar.paths" />
+ <classpath>
+ <pathelement location="${classesDirectory}/main" />
+ <pathelement location="${Etch.HOME}/lib/${etch-compiler.jar}" />
+ </classpath>
+ </javac>
+ -->
+
+ </sequential>
+ </macrodef>
+
+ <!-- MACRO: bundle-jars -->
+ <macrodef name="bundle-jars" >
+ <attribute name="dist" default="${Etch.dist}" />
+ <sequential>
+ <mkdir dir="@{dist}/lib" />
+
+ <!-- CREATE jars -->
+
+ <!-- Package up etch-java-compiler jar -->
+ <jar jarfile="@{dist}/lib/${etch-c-compiler.jar}" >
+ <manifest>
+ <attribute name="Copyright" value="${Etch.copyrightNotice}" />
+ <attribute name="Version" value="${Etch.version}" />
+ <attribute name="LongVersion" value="${Etch.longversion}" />
+ <attribute name="Build-Tag" value="${Etch.buildTag}" />
+ <attribute name="SVN-Revision" value="${Etch.runtime.revisionNumber}" />
+ </manifest>
+ <metainf dir="${Etch.basedir}" >
+ <include name="NOTICE.txt" />
+ <include name="LICENSE.txt" />
+ </metainf>
+ <fileset dir="${classesDirectory}/main">
+ <include name="org/apache/etch/bindings/**" />
+ <!-- <include name="resources/**" /> -->
+ </fileset>
+ </jar>
+
+ <!-- CREATE source archives -->
+
+ <!-- package up etch-java-compiler src -->
+ <zip destfile="@{dist}/lib/${etch-c-compiler-src.zip}" >
+ <fileset dir="${src}/main/java" >
+ <include name="org/apache/etch/bindings/**/*.java" />
+ </fileset>
+ <fileset dir="${src}/main/resources" >
+ <include name="**/*" />
+ </fileset>
+ </zip>
+
+ </sequential>
+ </macrodef>
+
+ <!-- INIT TARGET -->
+ <!-- Modify this target to define project specific properties that can only be set at runtime -->
+ <target name="do-init">
+ <delete dir="${target}" failonerror="false" quiet="true" />
+
+ <mkdir dir="${target}" />
+ <mkdir dir="${generatedSrc}" />
+ <mkdir dir="${classesDirectory}" />
+ <mkdir dir="${resourcesDirectory}" />
+ <mkdir dir="${testResultsDirectory}" />
+ </target>
+
+ <!-- CLEAN TARGET -->
+ <target name="do-clean">
+ <delete dir="${target}" />
+ </target>
+
+ <!-- BUILD TARGET -->
+
+ <target name="generate-sources" >
+ <!-- Generate version info -->
+ <update-tokens filename="${src}/main/java/org/apache/etch/bindings/c/compiler/CompilerVersion.java" />
+ </target>
+
+ <target name="compile-for-dist" >
+ <!-- Initialize target directories -->
+ <init-target />
+
+ <!-- Compile Source -->
+ <compile-sources />
+
+ <!-- Bundle Jars -->
+ <bundle-jars dist="${Etch.dist}" />
+ </target>
+
+ <target name="compile-for-clover" if="Clover.enabled" >
+
+ <echo message="Rebuilding with clover" />
+
+ <!-- initialize-clover -->
+ <initialize-clover suffix="etchccompiler" >
+ <fileset dir="${src}/main/java">
+ <include name="**/*.java" />
+ </fileset>
+ <!--
+ <testsources dir="${src}/test/java">
+ <include name="**/*.java" />
+ </testsources>
+ -->
+ </initialize-clover>
+
+ <!-- Initialize target directories -->
+ <init-target />
+
+ <!-- Compile Source -->
+ <compile-sources />
+
+ <!-- Bundle Jars -->
+ <bundle-jars dist="${Etch.clover-dist}" />
+
+ </target>
+
+ <target name="do-build" depends="generate-sources,compile-for-dist,compile-for-clover" />
+
+ <!-- TEST TARGET -->
+ <target name="do-test">
+
+ <!-- Run Unit Tests -->
+ <!--
+ <junit printsummary="yes" haltonfailure="no" dir="${classesDirectory}"
+ errorProperty="build.tests.fail" failureProperty="build.tests.fail">
+ <classpath>
+ <pathelement location="${classesDirectory}/main" />
+ <pathelement location="${classesDirectory}/test" />
+ <pathelement location="${Etch.dependency.junit.jar}" />
+ <pathelement location="${Etch.HOME}/lib/${etch-compiler.jar}" />
+ <pathelement location="${Etch.dependency.clover.jar}"/>
+ </classpath>
+ <formatter type="xml"/>
+ <batchtest fork="true" todir="${testResultsDirectory}">
+ <fileset dir="${src}/test/java">
+ <include name="**/*.java" />
+ </fileset>
+ </batchtest>
+ </junit>
+ -->
+ </target>
+
+ <!-- POSTBUILD TARGET -->
+ <target name="do-postbuild">
+ </target>
+
+ <target name="do-publish" if="build.tests.fail">
+ <!-- Set flag file if any tests failed -->
+ <touch file="${Etch.runtime.tests.fail}"/>
+ </target>
+
+</project>
diff --git a/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/Compiler.java b/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/Compiler.java
new file mode 100644
index 0000000..6ace32b
--- /dev/null
+++ b/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/Compiler.java
@@ -0,0 +1,1837 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.etch.bindings.c.compiler;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.etch.compiler.Backend;
+import org.apache.etch.compiler.CmdLineOptions;
+import org.apache.etch.compiler.EtchGrammar;
+import org.apache.etch.compiler.EtchGrammarConstants;
+import org.apache.etch.compiler.EtchHelper;
+import org.apache.etch.compiler.LogHandler;
+import org.apache.etch.compiler.Output;
+import org.apache.etch.compiler.ParseException;
+import org.apache.etch.compiler.Token;
+import org.apache.etch.compiler.Version;
+import org.apache.etch.compiler.ast.Builtin;
+import org.apache.etch.compiler.ast.Except;
+import org.apache.etch.compiler.ast.Item;
+import org.apache.etch.compiler.ast.Message;
+import org.apache.etch.compiler.ast.MessageDirection;
+import org.apache.etch.compiler.ast.Module;
+import org.apache.etch.compiler.ast.MsgDirHelper;
+import org.apache.etch.compiler.ast.Name;
+import org.apache.etch.compiler.ast.Named;
+import org.apache.etch.compiler.ast.ParamList;
+import org.apache.etch.compiler.ast.Parameter;
+import org.apache.etch.compiler.ast.ReservedWordChecker;
+import org.apache.etch.compiler.ast.Service;
+import org.apache.etch.compiler.ast.Struct;
+import org.apache.etch.compiler.ast.Thrown;
+import org.apache.etch.compiler.ast.TypeRef;
+import org.apache.etch.compiler.opt.ToString;
+import org.apache.etch.compiler.opt.ToString.FieldItem;
+import org.apache.etch.compiler.opt.ToString.FmtItem;
+import org.apache.etch.compiler.opt.ToString.StringItem;
+import org.apache.etch.util.Assertion;
+import org.apache.etch.util.Hash;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.runtime.log.LogChute;
+
+/**
+ * Compiler is a helper class not only for Backend, but also for the templates.
+ * They call methods here to perform "hard" tasks.
+ */
+public class Compiler extends Backend {
+
+ private final static String tmplPath1 = "org/apache/etch/bindings/c/compiler/";
+
+ private final static String tmplPath2 = "resources/org/apache/etch/bindings/c/compiler/";
+
+ private final static String fnSuffixH = ".h";
+ private final static String fnSuffixI = ".c";
+
+ private final static String VERSION = Version.VERSION + " / "
+ + CompilerVersion.VERSION;
+
+ /**
+ * Constructs the Compiler. This is a helper class not only for Backend, but
+ * also for the templates. They call methods here to perform "hard" tasks.
+ *
+ * @throws Exception
+ */
+ public Compiler() throws Exception {
+ initVelocity();
+
+ String[] path = { tmplPath1, tmplPath2 };
+
+ // value factory templates
+ vf_h_vm = getTemplate(path, "vf_h.vm");
+ vf_c_vm = getTemplate(path, "vf_c.vm");
+
+ // interface templates
+ intf_h_vm = getTemplate(path, "intf_h.vm");
+ intf_c_vm = getTemplate(path, "intf_c.vm");
+ implx_c_vm = getTemplate(path, "implx_c.vm");
+
+ // remote interface templates
+ remote_h_vm = getTemplate(path, "remote_h.vm");
+ remote_c_vm = getTemplate(path, "remote_c.vm");
+ remote_server_h_vm = getTemplate(path, "remote_server_h.vm");
+ remote_server_c_vm = getTemplate(path, "remote_server_c.vm");
+ remote_client_h_vm = getTemplate(path, "remote_client_h.vm");
+ remote_client_c_vm = getTemplate(path, "remote_client_c.vm");
+
+ // interface stub templates
+ stub_h_vm = getTemplate(path, "stub_h.vm");
+ stub_c_vm = getTemplate(path, "stub_c.vm");
+
+ // helper templates
+ helper_h_vm = getTemplate(path, "helper_h.vm");
+ helper_c_vm = getTemplate(path, "helper_c.vm");
+
+ // readme template
+ readme_vm = getTemplate(path, "readme.vm");
+
+ // Main template
+ main_h_vm = getTemplate(path, "main_h.vm");
+ main_c_vm = getTemplate(path, "main_c.vm");
+
+ // Base template
+ base_h_vm = getTemplate(path, "base_h.vm");
+ base_c_vm = getTemplate(path, "base_c.vm");
+
+ // Implementation template
+ impl_h_vm = getTemplate(path, "impl_h.vm");
+ impl_c_vm = getTemplate(path, "impl_c.vm");
+
+ etch_keywords_vm = getTemplate(path, "etch_keywords.vm");
+
+ // Keyword list
+ local_kwd = getPath(path, "cKeywords.kwd");
+ }
+
+ // Value Factory
+ private final Template vf_h_vm;
+ private final Template vf_c_vm;
+
+ // interface templates
+ private final Template intf_h_vm;
+ private final Template intf_c_vm;
+
+ // remote interface templates
+ private final Template remote_h_vm;
+ private final Template remote_c_vm;
+ private final Template remote_server_h_vm;
+ private final Template remote_server_c_vm;
+ private final Template remote_client_h_vm;
+ private final Template remote_client_c_vm;
+
+ // interface stub templates
+ private final Template stub_h_vm;
+ private final Template stub_c_vm;
+
+ // helper templates
+ private final Template helper_h_vm;
+ private final Template helper_c_vm;
+
+ // readme template
+ private final Template readme_vm;
+
+ // Main template
+ private final Template main_h_vm;
+ private final Template main_c_vm;
+
+ // Base template
+ private final Template base_h_vm;
+ private final Template base_c_vm;
+
+ // Implementation template
+ private final Template impl_h_vm;
+ private final Template impl_c_vm;
+ private final Template implx_c_vm;
+
+ private final Template etch_keywords_vm;
+
+ private final String local_kwd;
+
+ private LogHandler lh;
+
+ /**
+ * Initializes use of velocity engine and sets up resource loaders.
+ *
+ * @throws Exception
+ */
+ private void initVelocity() throws Exception {
+ Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, new MyLogger());
+
+ Velocity.setProperty(Velocity.RESOURCE_LOADER, "file, class");
+
+ Velocity.setProperty("file.resource.loader.description",
+ "Velocity File Resource Loader");
+ Velocity
+ .setProperty("file.resource.loader.class",
+ "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
+ Velocity.setProperty("file.resource.loader.path", ".");
+ Velocity.setProperty("class.resource.loader.description",
+ "Velocity Classpath Resource Loader");
+ Velocity
+ .setProperty("class.resource.loader.class",
+ "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
+ Velocity.init();
+ }
+
+ private static class MyLogger implements LogChute {
+ private final LogHandler lh = null;
+
+ public void init(RuntimeServices rts) throws Exception {
+ // ignore.
+ }
+
+ public boolean isLevelEnabled(int level) {
+ return level >= 2;
+ }
+
+ public void log(int level, String msg) {
+ if (level < 2)
+ return;
+
+ if (lh != null)
+ lh.report(level == 2 ? LogHandler.LEVEL_WARNING
+ : LogHandler.LEVEL_ERROR, null, msg);
+ else
+ System.out.printf("Velocity msg (%d): %s\n", level, msg);
+ }
+
+ public void log(int level, String msg, Throwable e) {
+ if (level < 2)
+ return;
+
+ if (lh != null)
+ lh.report(level == 2 ? LogHandler.LEVEL_WARNING
+ : LogHandler.LEVEL_ERROR, null, msg);
+ else
+ System.out.printf("Velocity msg (%d): %s: %s\n", level, msg, e);
+ }
+ }
+
+ /**
+ * @param path
+ * @param fn
+ * @return the velocity template
+ * @throws Exception
+ */
+ private Template getTemplate(String[] path, String fn) throws Exception {
+ ResourceNotFoundException rnfe = null;
+
+ for (String p : path) {
+ if (p == null)
+ continue;
+
+ // System.out.println( "trying to load template "+(p+fn) );
+ try {
+ if (Velocity.resourceExists(p + fn))
+ return Velocity.getTemplate(p + fn);
+ } catch (ResourceNotFoundException e) {
+ rnfe = e;
+ } catch (Exception e) {
+ System.out.println("ignoring " + e);
+ }
+ }
+
+ if (rnfe != null)
+ throw rnfe;
+
+ throw new ResourceNotFoundException("could not find resource: " + fn);
+ }
+
+ @Override
+ public void generate(Module module, CmdLineOptions options)
+ throws Exception {
+ // java always wants to not flatten packages:
+ options.noFlattenPackages = true;
+
+ lh = options.lh;
+
+ boolean ignoreGlobal = options.ignoreGlobalWordsList;
+ boolean ignoreLocal = options.ignoreLocalWordsList;
+ String userWords = options.userWordsList != null ? options.userWordsList
+ .getPath()
+ : null;
+ Set<String> what = options.what;
+
+ // Load the reserved words lists if any have been provided.
+ Map<String, String> words = new HashMap<String, String>();
+ if (!ignoreGlobal)
+ mapWords(global_kwd, words);
+ if (!ignoreLocal)
+ mapWords(local_kwd, words);
+ if (userWords != null)
+ mapWords(userWords, words);
+
+ // check for collisions with the reserved word list.
+ ReservedWordChecker checker = new ReservedWordChecker(words, false, lh);
+ module.treewalk(checker);
+ if (!checker.ok()) {
+ lh.report(LogHandler.LEVEL_ERROR, null,
+ "Encountered errors during c generation.");
+ return;
+ }
+
+ // ok, we're ready to generate code. make sure the
+ // output directories exist.
+
+ Output dir = options.output;
+ Output templateDir = options.templateOutput;
+
+ // generate code for each service.
+
+ for (Service intf : module) {
+ generate(intf, what, dir, templateDir);
+ }
+ }
+
+ public boolean isGenerateImpl() {
+ return isGenerateImpl;
+ }
+
+ public boolean isGenerateMain() {
+ return isGenerateMain;
+ }
+
+ boolean isGenerateImpl;
+ boolean isGenerateMain;
+
+ private void generate(final Service intf, Set<String> what, Output dir,
+ Output templateDir) throws Exception {
+ what = populateWhat(what);
+
+ if (what.isEmpty()) {
+ return;
+ }
+
+ final MessageDirection msgDir = getMessageDirection(what);
+
+ if (what.contains(WHAT_INTF)) {
+ // Generate the value factory file.
+
+ generateVf(intf, dir);
+
+ // Generate the interface, remote, and stub files.
+ generateIntfRemoteStub(intf, dir, msgDir, MessageDirection.BOTH,
+ false);
+ generateIntfRemoteStub(intf, dir, msgDir, MessageDirection.SERVER,
+ true);
+ generateIntfRemoteStub(intf, dir, msgDir, MessageDirection.CLIENT,
+ true);
+
+ // Generate helper file.
+ generateHelper(intf, dir, msgDir);
+
+ // Generate base file.
+ generateBase(intf, dir, msgDir, MessageDirection.SERVER, true);
+ generateBase(intf, dir, msgDir, MessageDirection.CLIENT, true);
+
+ // Generate readme file.
+ generateReadme(intf, dir, msgDir);
+
+ generateKeywordList(intf, dir, msgDir, MessageDirection.BOTH, true);
+ }
+
+ // Generate main template file.
+ if (what.contains(WHAT_MAIN)) {
+ isGenerateMain = true;
+ generateMain(intf, templateDir, msgDir);
+ }
+
+ // Generate impl template file.
+ if (what.contains(WHAT_IMPL)) {
+ isGenerateImpl = true;
+ generateImpl(intf, templateDir, msgDir);
+ }
+ }
+
+ private void generateReadme(final Service intf, Output dir,
+ final MessageDirection msgDir) throws Exception {
+ doFile(dir, "readme-etch-c-files.txt", lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateReadme(pw, intf, msgDir);
+ }
+ });
+ }
+
+ private void generateVf(final Service intf, Output dir) throws Exception {
+
+ doFile(dir, getVfName(intf) + fnSuffixH, lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateVfH(pw, intf);
+ }
+ });
+
+ doFile(dir, getVfName(intf) + fnSuffixI, lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateVfI(pw, intf);
+ }
+ });
+
+ }
+
+ private void generateHelper(final Service intf, Output dir,
+ final MessageDirection msgDir) throws Exception {
+ doFile(dir, getHelperName(intf) + fnSuffixH, lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateHelperH(pw, intf, msgDir);
+ }
+ });
+
+ doFile(dir, getHelperName(intf) + fnSuffixI, lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateHelperI(pw, intf, msgDir);
+ }
+ });
+
+ }
+
+ private void generateMain(final Service intf, Output dir,
+ MessageDirection msgDir) throws Exception {
+ if (msgDir == MessageDirection.BOTH
+ || msgDir == MessageDirection.CLIENT) {
+ doFile(dir, getMainName(intf, MessageDirection.CLIENT) + fnSuffixH,
+ lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateMainH(pw, intf, MessageDirection.CLIENT,
+ false);
+ }
+ });
+
+ doFile(dir, getMainName(intf, MessageDirection.CLIENT) + fnSuffixI,
+ lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateMainI(pw, intf, MessageDirection.CLIENT,
+ false);
+ }
+ });
+
+ }
+
+ if (msgDir == MessageDirection.BOTH
+ || msgDir == MessageDirection.SERVER) {
+ doFile(dir, getMainName(intf, MessageDirection.SERVER) + fnSuffixH,
+ lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateMainH(pw, intf, MessageDirection.SERVER,
+ false);
+ }
+ });
+
+ doFile(dir, getMainName(intf, MessageDirection.SERVER) + fnSuffixI,
+ lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateMainI(pw, intf, MessageDirection.SERVER,
+ false);
+ }
+ });
+ }
+ }
+
+ private void generateKeywordList(final Service intf, Output dir,
+ final MessageDirection what, final MessageDirection mc,
+ final boolean hasBaseClass) throws Exception {
+ doFile(dir, getKeywordFilename(intf), lh,
+ new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateKeywords(pw, intf, mc, hasBaseClass);
+ }
+ });
+ }
+
+ private void generateBase(final Service intf, Output dir,
+ final MessageDirection what, final MessageDirection mc,
+ final boolean hasBaseClass) throws Exception {
+ doFile(dir, getBaseFileNameH(intf, getDirectionName(mc)), lh,
+ new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateBaseH(pw, intf, mc, hasBaseClass);
+ }
+ });
+
+ doFile(dir, getBaseFileNameI(intf, getDirectionName(mc)), lh,
+ new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateBaseI(pw, intf, mc, hasBaseClass);
+ }
+ });
+ }
+
+ private void generateImpl(final Service intf, Output dir,
+ MessageDirection msgDir) throws Exception {
+ if (msgDir == MessageDirection.BOTH
+ || msgDir == MessageDirection.CLIENT) {
+ doFile(dir, getImplFileNameH(intf,
+ getDirectionName(MessageDirection.CLIENT)), lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateImplH(pw, intf, MessageDirection.CLIENT, false);
+ }
+ });
+
+ doFile(dir, getImplFileNameI(intf,
+ getDirectionName(MessageDirection.CLIENT)), lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateImplI(pw, intf, MessageDirection.CLIENT, false);
+ }
+ });
+
+ doFile(dir, getImplXFileNameI(intf,
+ getDirectionName(MessageDirection.CLIENT)), lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateImplXI(pw, intf, MessageDirection.CLIENT, false);
+ }
+ });
+ }
+
+ if (msgDir == MessageDirection.BOTH
+ || msgDir == MessageDirection.SERVER) {
+ doFile(dir, getImplFileNameH(intf,
+ getDirectionName(MessageDirection.SERVER)), lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateImplH(pw, intf, MessageDirection.SERVER, false);
+ }
+ });
+
+ doFile(dir, getImplFileNameI(intf,
+ getDirectionName(MessageDirection.SERVER)), lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateImplI(pw, intf, MessageDirection.SERVER, false);
+ }
+ });
+
+ doFile(dir, getImplXFileNameI(intf,
+ getDirectionName(MessageDirection.SERVER)), lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateImplXI(pw, intf, MessageDirection.SERVER, false);
+ }
+ });
+ }
+
+ }
+
+ private void generateIntfRemoteStub(final Service intf, Output dir,
+ final MessageDirection what, final MessageDirection mc,
+ final boolean hasBaseClass) throws Exception {
+ // Generate interface file
+
+ if (mc == MessageDirection.BOTH) {
+ doFile(dir, getIntfFileNameH(intf), lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateIntfH(pw, intf, mc, hasBaseClass);
+ }
+ });
+
+ doFile(dir, getIntfFileNameI(intf), lh, new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateIntfI(pw, intf, mc, hasBaseClass);
+ }
+ });
+ }
+
+ // Generate remote file
+
+ if (mc == MessageDirection.BOTH || what == MessageDirection.BOTH
+ || mc != what) {
+ doFile(dir, getRemoteFileNameH(intf, getDirectionName(mc)), lh,
+ new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateRemoteH(pw, intf, mc, hasBaseClass);
+ }
+ });
+
+ doFile(dir, getRemoteFileNameI(intf, getDirectionName(mc)), lh,
+ new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateRemoteI(pw, intf, mc, hasBaseClass);
+ }
+ });
+ }
+
+ // Generate stub file
+
+ if (mc != what) {
+ doFile(dir, getStubFileNameH(intf, getDirectionName(mc)), lh,
+ new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateStubH(pw, intf, mc, hasBaseClass);
+ }
+ });
+
+ doFile(dir, getStubFileNameI(intf, getDirectionName(mc)), lh,
+ new Gen() {
+ public void run(PrintWriter pw) throws Exception {
+ generateStubI(pw, intf, mc, hasBaseClass);
+ }
+ });
+ }
+ }
+
+ /**
+ * Generate the value factory header for the service.
+ *
+ * @param pw
+ * @param intf
+ * @throws Exception
+ */
+ void generateVfH(PrintWriter pw, Service intf) throws Exception {
+ // params keeps track of the total set of parameters
+ // named (for enums, structs, exceptions, and messages).
+ Set<String> params = new HashSet<String>();
+
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("params", params);
+ vf_h_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the value factory implementation for the service.
+ *
+ * @param pw
+ * @param intf
+ * @throws Exception
+ */
+ void generateVfI(PrintWriter pw, Service intf) throws Exception {
+ // params keeps track of the total set of parameters
+ // named (for enums, structs, exceptions, and messages).
+ Set<String> params = new HashSet<String>();
+
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("params", params);
+ vf_c_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the interface header for the service.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateIntfH(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ intf_h_vm.merge(context, pw);
+
+ }
+
+ /**
+ * Generate the interface implementation for the service.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateIntfI(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ intf_c_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the call to message implementation of the interface. This class
+ * turns calls on its methods into messages which are sent to the remote
+ * stub. For two-way calls, it then waits for a response message, returning
+ * the result therein to the caller.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateRemoteH(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ context.put("methodList", new ArrayList<String>());
+ switch (mc) {
+ case BOTH:
+ remote_h_vm.merge(context, pw);
+ break;
+ case SERVER:
+ remote_server_h_vm.merge(context, pw);
+ break;
+ case CLIENT:
+ remote_client_h_vm.merge(context, pw);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Generate the call to message implementation of the interface. This class
+ * turns calls on its methods into messages which are sent to the remote
+ * stub. For two-way calls, it then waits for a response message, returning
+ * the result therein to the caller.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateRemoteI(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ context.put("methodList", new ArrayList<String>());
+ switch (mc) {
+ case BOTH:
+ remote_c_vm.merge(context, pw);
+ break;
+ case SERVER:
+ remote_server_c_vm.merge(context, pw);
+ break;
+ case CLIENT:
+ remote_client_c_vm.merge(context, pw);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Generate the message to call implementation. This class accepts a message
+ * and turns it back into a call on the user's implementation. For two-way
+ * messages, the return value from the user's implementation method is
+ * turned into the appropriate response message and sent.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateStubH(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("suffix_remote", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ stub_h_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the message to call implementation. This class accepts a message
+ * and turns it back into a call on the user's implementation. For two-way
+ * messages, the return value from the user's implementation method is
+ * turned into the appropriate response message and sent.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateStubI(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("suffix_remote", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ stub_c_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the transport plumbing helper.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @throws Exception
+ */
+ void generateHelperH(PrintWriter pw, Service intf, MessageDirection mc)
+ throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ helper_h_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the transport plumbing helper.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @throws Exception
+ */
+ void generateHelperI(PrintWriter pw, Service intf, MessageDirection mc)
+ throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ helper_c_vm.merge(context, pw);
+
+ }
+
+ /**
+ * Generate the readme.txt.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @throws Exception
+ */
+ void generateReadme(PrintWriter pw, Service intf, MessageDirection mc)
+ throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+
+ readme_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the template main program.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateMainH(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ main_h_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the template main program.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateMainI(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ main_c_vm.merge(context, pw);
+ }
+
+ /**
+ * Generates the base implementation of the interfaces, with each method
+ * throwing an exception to the tune that it isn't implemented. User's impl
+ * will extend this base implementation.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateBaseH(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ context.put("methodList", new ArrayList<String>());
+ base_h_vm.merge(context, pw);
+ }
+
+ /**
+ * Generates the base implementation of the interfaces, with each method
+ * throwing an exception to the tune that it isn't implemented. User's impl
+ * will extend this base implementation.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateBaseI(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ context.put("methodList", new ArrayList<String>());
+ base_c_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the template user implemention class which extends the base
+ * implementation generated above. This class will only have the appropriate
+ * constructor and reference to the appropriate remote, and a comment
+ * inviting the user to override methods.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateImplH(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ impl_h_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the template user implemention class which extends the base
+ * implementation generated above. This class will only have the appropriate
+ * constructor and reference to the appropriate remote, and a comment
+ * inviting the user to override methods.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateImplI(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ impl_c_vm.merge(context, pw);
+ }
+
+ void generateKeywords(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ etch_keywords_vm.merge(context, pw);
+ }
+
+ /**
+ * Generate the template user implemention class which extends the base
+ * implementation generated above. This class will only have the appropriate
+ * constructor and reference to the appropriate remote, and a comment
+ * inviting the user to override methods.
+ *
+ * @param pw
+ * @param intf
+ * @param mc
+ * @param hasBaseClass
+ * @throws Exception
+ */
+ void generateImplXI(PrintWriter pw, Service intf, MessageDirection mc,
+ boolean hasBaseClass) throws Exception {
+ VelocityContext context = new VelocityContext();
+ context.put("now", new Date());
+ context.put("version", VERSION);
+ context.put("helper", this);
+ context.put("intf", intf);
+ context.put("mc", mc);
+ context.put("suffix", MsgDirHelper.getSuffix(mc).toLowerCase());
+ context.put("hasBaseClass", hasBaseClass);
+ implx_c_vm.merge(context, pw);
+ }
+
+ public String getKeywordForWireshark(String fieldname) {
+ int hash = Hash.hash(fieldname);
+ return String.format("0x%08x", hash) + "," + fieldname;
+
+ }
+
+
+
+ public MessageDirection getRemoteDirection(MessageDirection mc) {
+ if (mc == MessageDirection.SERVER) {
+ return MessageDirection.CLIENT;
+ }
+ if (mc == MessageDirection.BOTH) {
+ return MessageDirection.BOTH;
+ }
+ return MessageDirection.SERVER;
+ }
+
+ public String getRemoteDirectionName(MessageDirection mc) {
+
+ return getDirectionName(getRemoteDirection(mc));
+ }
+
+ public boolean hasMessageDirectionBoth(Service intf) {
+ return intf.hasMessageDirection(MessageDirection.BOTH);
+ }
+
+ public boolean isClient(MessageDirection mc) {
+ return mc == MessageDirection.CLIENT || mc == MessageDirection.BOTH;
+ }
+
+ public boolean isServer(MessageDirection mc) {
+ return mc == MessageDirection.SERVER || mc == MessageDirection.BOTH;
+ }
+
+ public String getDirectionName(MessageDirection mc) {
+ return MsgDirHelper.getSuffix(mc).toLowerCase();
+ }
+
+ public MessageDirection getMsgDirServer(){
+ return MessageDirection.SERVER;
+ }
+
+ public MessageDirection getMsgDirClient(){
+ return MessageDirection.CLIENT;
+ }
+
+ public String getServiceName(Service intf) {
+ return intf.name().name().toLowerCase();
+ }
+
+ public String getIntfName(Service intf) {
+ return getServiceName(intf);
+ }
+
+ public String getIntfFileNameH(Service intf) {
+ return getIntfName(intf) + "_interface" + fnSuffixH;
+ }
+
+ public String getIntfFileNameI(Service intf) {
+ return getIntfName(intf) + "_interface" + fnSuffixI;
+ }
+
+ public String getBaseName(Service intf, String suffix) {
+ return getServiceName(intf)
+ + (suffix != null && !suffix.isEmpty() ? "_" + suffix : "");
+ }
+
+ public String getBaseFileNameH(Service intf, String suffix) {
+ return getBaseName(intf, suffix) + fnSuffixH;
+ }
+
+ public String getKeywordFilename(Service intf) {
+ return getServiceName(intf) + "_keywords_wireshark.txt";
+ }
+
+ public String getBaseFileNameI(Service intf, String suffix) {
+ return getBaseName(intf, suffix) + fnSuffixI;
+ }
+
+ public String getRemoteName(Service intf, String suffix) {
+ return getServiceName(intf) + "_remote"
+ + (suffix != null && !suffix.isEmpty() ? "_" + suffix : "");
+ }
+
+ public String getRemoteFileNameH(Service intf, String suffix) {
+ return getRemoteName(intf, suffix) + fnSuffixH;
+ }
+
+ public String getRemoteFileNameI(Service intf, String suffix) {
+ return getRemoteName(intf, suffix) + fnSuffixI;
+ }
+
+ public String getStubName(Service intf, String suffix) {
+ return getServiceName(intf)
+ + (suffix != null && !suffix.isEmpty() ? "_" + suffix : "")
+ + "_stub";
+ }
+
+ public String getStubFileNameH(Service intf, String suffix) {
+ return getStubName(intf, suffix) + fnSuffixH;
+ }
+
+ public String getStubFileNameI(Service intf, String suffix) {
+ return getStubName(intf, suffix) + fnSuffixI;
+ }
+
+ public String getImplName(Service intf, String suffix) {
+ return getServiceName(intf)
+ + (suffix != null && !suffix.isEmpty() ? "_" + suffix : "")
+ + "_impl";
+ }
+
+ public String getImplFileNameH(Service intf, String suffix) {
+ return getImplName(intf, suffix) + fnSuffixH;
+ }
+
+ public String getImplFileNameI(Service intf, String suffix) {
+ return getImplName(intf, suffix) + fnSuffixI;
+ }
+
+ public String getImplXName(Service intf, String suffix) {
+ return getServiceName(intf)
+ + (suffix != null && !suffix.isEmpty() ? "_" + suffix : "")
+ + "_implx";
+ }
+
+ public String getImplXFileNameH(Service intf, String suffix) {
+ return getImplXName(intf, suffix) + fnSuffixH;
+ }
+
+ public String getImplXFileNameI(Service intf, String suffix) {
+ return getImplXName(intf, suffix) + fnSuffixI;
+ }
+
+ public String getVfName(Service intf) {
+ return getServiceName(intf) + "_valufact";
+ }
+
+ public String getVfFileNameH(Service intf) {
+ return getVfName(intf) + fnSuffixH;
+ }
+
+ public String getVfFileNameI(Service intf) {
+ return getVfName(intf) + fnSuffixI;
+ }
+
+ public String getMainName(Service intf, MessageDirection mc) {
+ if (mc == MessageDirection.SERVER)
+ return getServiceName(intf) + "_listener_main";
+ return getServiceName(intf) + "_client_main";
+ }
+
+ public String getMainFileNameH(Service intf, MessageDirection mc) {
+ return getMainName(intf, mc) + fnSuffixH;
+ }
+
+ public String getMainFileNameI(Service intf, MessageDirection mc) {
+ return getMainName(intf, mc) + fnSuffixI;
+ }
+
+ public String getHelperName(Service intf) {
+ return getServiceName(intf) + "_helper";
+ }
+
+ public String getHelperFileNameH(Service intf) {
+ return getHelperName(intf) + fnSuffixH;
+ }
+
+ public String getHelperFileNameI(Service intf) {
+ return getHelperName(intf) + fnSuffixI;
+ }
+
+ public void test(TypeRef type) {
+ int i;
+ System.out.println(type);
+ System.out.println("builtin " + type.isBuiltin());
+ type.getNamed(type.intf()).efqname(this);
+
+ }
+
+ public void testMsg(Message msg) {
+ int i;
+ // msg.getResultParam().efqname(this);
+ // msg.type().getNamed(msg.service()).efqname(this)
+ msg.getResultParam().efqname(this);
+ }
+
+ @Override
+ public String asyncReceiverPoolName(Message msg) {
+ return msg.getAsyncReceiver().toString().toLowerCase();
+ }
+
+ @Override
+ public String getTypeValue(TypeRef type, Token value) {
+ Token t = type.type();
+ switch (t.kind) {
+ case EtchGrammarConstants.LONG:
+ return value.image + "LL";
+ case EtchGrammarConstants.FLOAT:
+ case EtchGrammarConstants.DOUBLE:
+ return value.image + (value.image.indexOf('.')>-1 ? "f" : "");
+ case EtchGrammarConstants.STRING:
+ return "L" + protectString(value.image);
+ case EtchGrammarConstants.BOOLEAN:
+ return value.image.toUpperCase();
+ default:
+ return value.image;
+ }
+ }
+
+ private String protectString(String s) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("\"");
+ for (char c : s.toCharArray()) {
+ if (c == '\t') {
+ sb.append("\\t");
+ continue;
+ }
+ if (c == '\r') {
+ sb.append("\\r");
+ continue;
+ }
+ if (c == '\n') {
+ sb.append("\\n");
+ continue;
+ }
+ if (c == '\"') {
+ sb.append("\\\"");
+ continue;
+ }
+ if (c == '\\') {
+ sb.append("\\\\");
+ continue;
+ }
+ if (c >= 32 && c < 127) {
+ sb.append(c);
+ continue;
+ }
+ sb.append(String.format("\\u%04x", (int) c));
+ }
+ sb.append("\"");
+ return sb.toString();
+ }
+
+ @Override
+ public String getTypeName(TypeRef type) {
+ if (type.dim() > 0)
+ return "etch_arraytype*";
+ return getPointerTypeName(type);
+ }
+
+ public String getNativeTypeNameForConstants(TypeRef type) {
+ if (type.type().kind == EtchGrammarConstants.STRING)
+ return "wchar_t*";
+ return getNativeTypeName(type, false);
+ }
+
+ /**
+ * @param type
+ * the etch type
+ * @return the fundamental native type for c so etch int -> c int, while
+ * etch string -> wchar* string.
+ */
+ @Override
+ public String getNativeTypeName(TypeRef type) {
+ return getNativeTypeName(type, false);
+ }
+
+ public String getNativeTypeName(TypeRef type, boolean addStruct) {
+ return getNativeTypeName(type, false, false);
+ }
+
+ public String getEtchTypeName(TypeRef type) {
+ return getNativeTypeName(type, false, true);
+ }
+
+ public String getNativeTypeName(TypeRef type, boolean addStruct, boolean etch_type) {
+ if (type.isArray()) {
+ return (etch_type ? "etch_arraytype" : "etch_arraytype*");
+ }
+
+ Token t = type.type();
+ switch (t.kind) {
+ case EtchGrammarConstants.VOID:
+ return "void";
+ case EtchGrammarConstants.BOOLEAN:
+ return (etch_type ? "etch_boolean" : "boolean");
+ case EtchGrammarConstants.BYTE:
+ return (etch_type ? "etch_int8" : "byte");
+ case EtchGrammarConstants.SHORT:
+ return (etch_type ? "etch_int16" : "short");
+ case EtchGrammarConstants.INT:
+ return (etch_type ? "etch_int32" : "int");
+ case EtchGrammarConstants.LONG:
+ return (etch_type ? "etch_int64" : "int64");
+ case EtchGrammarConstants.FLOAT:
+ return (etch_type ? "etch_float" : "float");
+ case EtchGrammarConstants.DOUBLE:
+ return (etch_type ? "etch_double" : "double");
+ case EtchGrammarConstants.STRING:
+ return (etch_type ? "etch_string" : "etch_string*");
+ case EtchGrammarConstants.OBJECT:
+ return (etch_type ? "etch_object" : "etch_object*");
+ default: {
+ // we have to use a fully qualified name here.
+ // find the actual type...
+ Named<?> n = type.intf().get(t.image);
+ System.out.println("Token:" + t.image);
+ if (n == null)
+ throw new IllegalArgumentException(String.format(
+ "undefined or ambiguous name at line %d: %s",
+ t.beginLine, t.image));
+
+
+ if (n.isEnumx())
+ {
+ return n.efqname(this) + (etch_type ? "" : "_enum");
+ }
+ else
+ {
+ return (addStruct ? "struct " : "") + n.efqname(this) + (etch_type ? "" : "*");
+ }
+ }
+ }
+ }
+
+ /**
+ * @param type
+ * the etch type
+ * @return the fundamental native reference type for c. so etch int -> java
+ * etch_int32*, while etch string -> etch_string*.
+ */
+ public String getPointerTypeName(TypeRef type) {
+ if (type.isArray())
+ return "etch_arraytype*";
+ Token t = type.type();
+
+ switch (t.kind) {
+ case EtchGrammarConstants.VOID:
+ return "void*";
+ case EtchGrammarConstants.BOOLEAN:
+ return "etch_boolean*";
+ case EtchGrammarConstants.BYTE:
+ return "etch_byte*";
+ case EtchGrammarConstants.SHORT:
+ return "etch_int16*";
+ case EtchGrammarConstants.INT:
+ return "etch_int32*";
+ case EtchGrammarConstants.LONG:
+ return "etch_int64*";
+ case EtchGrammarConstants.FLOAT:
+ return "etch_float*";
+ case EtchGrammarConstants.DOUBLE:
+ return "etch_double*";
+ case EtchGrammarConstants.STRING:
+ return "etch_string*";
+ case EtchGrammarConstants.OBJECT:
+ return "etch_object*";
+ default: {
+ // we have to use a fully qualified name here.
+ // find the actual type...
+ Named<?> n = type.intf().get(t.image);
+ if (n == null)
+ throw new IllegalArgumentException(String.format(
+ "undefined or ambiguous name at line %d: %s",
+ t.beginLine, t.image));
+ return n.efqname(this) + "*";
+ }
+ }
+ }
+
+ @Override
+ public String mfvname(String vname) {
+ return "_mf_" + vname;
+ }
+
+ @Override
+ public String mtvname(String vname) {
+ return "_mt_" + vname;
+ }
+
+ @Override
+ public String getLang() {
+ return "c";
+ }
+
+ @Override
+ public String enum_efqname(String fqname, String moduleName,
+ String serviceName, String enumName) {
+ // return serviceName.toLowerCase() + "_" + enumName.toLowerCase();
+ return serviceName.toLowerCase() + "_" + enumName;
+ }
+
+ @Override
+ public String except_efqname(String fqname, String moduleName,
+ String serviceName, String exceptName) {
+ // return serviceName.toLowerCase() + "_" + exceptName.toLowerCase();
+ return serviceName.toLowerCase() + "_" + exceptName;
+ }
+
+ @Override
+ public String struct_efqname(String fqname, String moduleName,
+ String serviceName, String structName) {
+ // return serviceName.toLowerCase() + "_" + structName.toLowerCase();
+ return serviceName.toLowerCase() + "_" + structName;
+ }
+
+ @Override
+ public String qualifyParameterName(Token name) {
+ return name.image;
+ }
+
+ @Override
+ public String qualifyConstantName(Service intf, Token name) {
+ return intf.fqname() + '.' + name.image;
+ }
+
+ @Override
+ public String qualifyEnumName(Service intf, Token name) {
+ return intf.fqname() + '.' + name.image;
+ }
+
+ Set<String> history = new HashSet<String>();
+
+ public boolean resetHistory() {
+ history = new HashSet<String>();
+ return true;
+ }
+
+ public boolean addStringToHistory(String s) {
+ history.add(s);
+ return true;
+ }
+
+ public boolean historyContains(String s) {
+ return history.contains(s);
+ }
+
+ // ///////////////////////////////////////////////
+ // /////////////////// WORKSPACE /////////////////
+ // ///////////////////////////////////////////////
+
+ @Override
+ public String formatString(ParamList<Service> n, boolean isExcept)
+ throws ParseException, IOException {
+ ToString ts = (ToString) n.getOpt("ToString");
+ List<FmtItem> list;
+ if (ts != null) {
+ list = ts.getFormat();
+ n.checkFormatList(ts.lineno(), list);
+ } else if (isExcept)
+ list = n.mkFormatList(true, ((Except) n).hasExtends());
+ else
+ list = n.mkFormatList(false, ((Struct) n).hasExtends());
+
+ if (list.size() == 1) {
+ return list.get(0).value();
+ }
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("String.format( ");
+ sb.append("\"");
+ for (FmtItem i : list) {
+ if (i instanceof FieldItem) {
+ sb.append("%s");
+ } else {
+ escape(sb, ((StringItem) i).value());
+ }
+ }
+ sb.append("\"");
+ for (FmtItem i : list) {
+ if (i instanceof FieldItem) {
+ sb.append(", ");
+ sb.append(((FieldItem) i).value());
+ }
+ }
+ sb.append(" )");
+ return sb.toString();
+ }
+
+ private void escape(StringBuffer sb, String s) throws IOException {
+ StringReader rdr = new StringReader(s);
+ int c;
+ while ((c = rdr.read()) >= 0) {
+ if (c == '"')
+ sb.append("\\\"");
+ else if (c == '\\')
+ sb.append("\\\\");
+ else if (c == '\t')
+ sb.append("\\t");
+ else if (c == '\r')
+ sb.append("\\r");
+ else if (c == '\n')
+ sb.append("\\n");
+ else
+ sb.append((char) c);
+ }
+ }
+
+ public String getValidator(Service service, Named<?> named) {
+
+// (objmask*) etchvtor_custom_get(ETCHTYPEB_USER,
+// get_dynamic_classid_unique(&CLASSID_TESTER_SIMPLESTRUCT),
+// tester_valufact_get_static()->_mt_tester_simpleStruct,
+// 1))
+
+ if (named instanceof Parameter) {
+ Parameter param = (Parameter) named;
+ TypeRef type = param.type();
+
+ if (type.isBuiltin())
+ return String.format("(etch_object*)etchvtor_%s_get( %d )", getValidatorStringForParam(param),
+ type.dim());
+
+ Named<?> n = type.getNamed(type.intf());
+
+ if (n.isBuiltin()) {
+ Builtin b = (Builtin) n;
+ String cn = b.className();
+
+ int i = cn.indexOf('<');
+ if (i >= 0)
+ cn = cn.substring(0, i);
+
+ String classid = getClassIDBuiltin(b);
+ String messagetype = getMessageTypeBuiltin(b);
+ return String.format(
+ "(etch_object*)etchvtor_custom_get(%s, CLASSID_%s, builtins._mt_%s,%d)",
+ getEtchTypeForBuiltin(b),classid, messagetype, getDimensionForBuiltin(b));
+ }
+
+
+ if (n.isStruct() || n.isExcept() || n.isEnumx())
+ return String.format(
+ "(etch_object*)etchvtor_custom_get(ETCHTYPEB_USER, get_dynamic_classid_unique(&CLASSID_%s), %s_valufact_get_static()->_mt_%s, %d)",
+ n.efqname(this).toUpperCase(), service.name().toString().toLowerCase(), n.efqname(this), type.dim());
+
+ if (n.isExtern())
+ {
+ Assertion.check(false,"extern type not supported for " + n );
+ }
+ else
+ {
+ Assertion.check(false,"unknown type " + n);
+ }
+
+ return "";
+ }
+
+ if (named instanceof Thrown) {
+ Thrown thrown = (Thrown) named;
+ Except e = (Except) thrown.getNamed();
+ return String.format("(etch_object*)etchvtor_custom_get( ETCHTYPEB_USER,get_dynamic_classid_unique(&%s), %s_valufact_get_static()->_mt_%s_%s,0)",
+ getClassIdVarName(thrown),getExcept(thrown).service().name().toString().toLowerCase(),getExcept(thrown).service().name().toString().toLowerCase(),thrown);
+ }
+
+ if (named instanceof Item)
+ return "(etch_object*)etchvtor_boolean_get( 0 )";
+
+ return "null";
+ }
+
+ private int getDimensionForBuiltin(Builtin b) {
+ if(b.bindingName().equals("etch_date"))
+ return 0;
+ if(b.bindingName().equals("etch_arraylist"))
+ return 1;
+ if(b.bindingName().equals("etch_hashtable"))
+ return 0;
+ if(b.bindingName().equals("etch_set"))
+ return 0;
+
+ return 0;
+ }
+
+ private String getEtchTypeForBuiltin(Builtin b) {
+ if(b.bindingName().equals("etch_date"))
+ return "ETCHTYPEB_PRIMITIVE";
+ if(b.bindingName().equals("etch_arraylist"))
+ return "ETCHTYPEB_ETCHLIST";
+ if(b.bindingName().equals("etch_hashtable"))
+ return "ETCHTYPEB_ETCHMAP";
+ if(b.bindingName().equals("etch_set"))
+ return "ETCHTYPEB_ETCHSET";
+
+ return "ETCHTYPEB_USER";
+
+ }
+
+ private String getMessageTypeBuiltin(Builtin b) {
+ if(b.bindingName().equals("etch_date"))
+ return "_etch_datetime";
+ if(b.bindingName().equals("etch_arraylist"))
+ return "_etch_list";
+ if(b.bindingName().equals("etch_hashtable"))
+ return "_etch_map";
+ if(b.bindingName().equals("etch_set"))
+ return "_etch_set";
+ return "NULL";
+ }
+
+ private String getClassIDBuiltin(Builtin b) {
+ if(b.bindingName().equals("etch_date"))
+ return "DATE";
+ if(b.bindingName().equals("etch_arraylist"))
+ return "ETCH_LIST";
+ if(b.bindingName().equals("etch_hashtable"))
+ return "ETCH_MAP";
+ if(b.bindingName().equals("etch_set"))
+ return "ETCH_SET";
+
+ return "NULL";
+ }
+
+ /**
+ * @param name
+ * @return the appropriate name for a getter method.
+ */
+ public String getGetterName(Name name) {
+ String s = name.name;
+ return "get" + s.substring(0, 1).toUpperCase() + s.substring(1);
+ }
+
+ /**
+ * @param name
+ * @return the appropriate name for a setter method.
+ */
+ public String getSetterName(Name name) {
+ String s = name.name;
+ return "set" + s.substring(0, 1).toUpperCase() + s.substring(1);
+ }
+
+ @Override
+ public void addDefaults(Service service) throws ParseException {
+ addBuiltin( service, newName( "List" ), "etch_arraylist", true );
+ addBuiltin( service, newName( "Map" ), "etch_hashtable", true );
+ addBuiltin( service, newName( "Set" ), "etch_set", true );
+ addBuiltin(service, newName("Datetime"), "etch_date", false);
+ }
+
+ public String getValidatorStringForParam(Parameter param) {
+ TypeRef type = param.type();
+ return getValidatorStringForParam(type);
+ }
+
+ public String getValidatorStringForParam(TypeRef type) {
+ switch (type.type().kind) {
+ case EtchGrammarConstants.BOOLEAN:
+ return "boolean";
+ case EtchGrammarConstants.BYTE:
+ return "byte";
+ case EtchGrammarConstants.INT:
+ return "int32";
+ case EtchGrammarConstants.SHORT:
+ return "int16";
+ case EtchGrammarConstants.DOUBLE:
+ return "double";
+ case EtchGrammarConstants.FLOAT:
+ return "float";
+ case EtchGrammarConstants.LONG:
+ return "int64";
+ case EtchGrammarConstants.STRING:
+ return "string";
+ }
+ // what should be the default here?
+ return "object";
+ }
+
+ public String getValidatorStringForParam(Message param) {
+ return getValidatorStringForParam(param.type());
+ }
+
+ public boolean isEnumParam(Parameter p) {
+ if (p.type() == null)
+ return false;
+ if (p.type().intf() == null)
+ return false;
+ if (p.type().getNamed(p.type().intf()) == null)
+ return false;
+ return p.type().getNamed(p.type().intf()).isEnumx();
+ }
+
+ public boolean isCustomType(Parameter param) {
+ return isCustomType(param.type());
+ }
+
+ public boolean isRefType(TypeRef param) {
+ if(param == null)
+ return true;
+ switch(param.type().kind) {
+ case EtchGrammarConstants.BOOLEAN:
+ case EtchGrammarConstants.DOUBLE:
+ case EtchGrammarConstants.FLOAT:
+ case EtchGrammarConstants.INT:
+ case EtchGrammarConstants.LONG:
+ case EtchGrammarConstants.ENUM:
+ case EtchGrammarConstants.DECIMAL:
+ case EtchGrammarConstants.SHORT:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ public boolean isCustomType(TypeRef param) {
+ switch (param.type().kind) {
+ case EtchGrammarConstants.BOOLEAN:
+ case EtchGrammarConstants.DOUBLE:
+ case EtchGrammarConstants.FLOAT:
+ case EtchGrammarConstants.INTEGER:
+ case EtchGrammarConstants.STRING:
+ case EtchGrammarConstants.LONG:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ public String getTypeName(Parameter pa) {
+ return pa.type().type().image;
+ }
+
+ public String getTypeName(Message pa) {
+ return pa.type().type().image;
+ }
+
+ public List<String> getUsedServiceNames(Service s) {
+ final List<String> ret = new LinkedList<String>();
+ IncludeTreeWalker walker = new IncludeTreeWalker(ret, s);
+ try {
+ s.treewalk(walker);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return walker.getList();
+ }
+
+ public boolean containsStructsOrMessages(Service s) {
+ return s.messages(false).hasNext() || s.structs(false).hasNext();
+ }
+
+ public String getClassIdVarName(Parameter p) {
+ Named<?> n = p.type().getNamed(p.type().intf());
+ return "CLASSID_" + n.efqname(this).toUpperCase();
+ }
+
+ public String getClassIdVarName(Thrown t) {
+ Except p = (Except) t.getNamed();
+ return "CLASSID_" + p.service().name().toString().toUpperCase() + "_"
+ + p.name().toString().toUpperCase();
+ }
+
+ public Except getExcept(Thrown t) {
+ Except e = (Except) t.getNamed();
+ return e;
+ }
+
+ public String getClassIdVarName(Message n) {
+ Named<?> na = n.type().getNamed(n.type().intf());
+ return "CLASSID_" + na.efqname(this).toUpperCase();
+ }
+
+ public String getDefiningServiceNameOf(TypeRef type) {
+ Named<?> na = type.getNamed(type.intf());
+ return na.parent().name().name.toLowerCase();
+ }
+
+ @Override
+ public String getValidator(Named<?> named) {
+ return getValidator(null, named);
+ }
+
+}
diff --git a/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/CompilerVersion.java b/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/CompilerVersion.java
new file mode 100644
index 0000000..0fd9770
--- /dev/null
+++ b/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/CompilerVersion.java
@@ -0,0 +1,33 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.etch.bindings.c.compiler;
+
+/**
+ * The version info of this Etch backend (compiler).
+ */
+public interface CompilerVersion
+{
+ // This file is edited by the production build system to replace the value
+ // of VERSION below with whatever it wants the version string to actually be.
+
+ /** The version of this Etch backend (compiler) */
+ public String VERSION = "c 1.1.0-incubating (LOCAL-0)";
+}
diff --git a/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/CompilerVersion.java.tmpl b/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/CompilerVersion.java.tmpl
new file mode 100644
index 0000000..973d785
--- /dev/null
+++ b/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/CompilerVersion.java.tmpl
@@ -0,0 +1,33 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.etch.bindings.c.compiler;
+
+/**
+ * The version info of this Etch backend (compiler).
+ */
+public interface CompilerVersion
+{
+ // This file is edited by the production build system to replace the value
+ // of VERSION below with whatever it wants the version string to actually be.
+
+ /** The version of this Etch backend (compiler) */
+ public String VERSION = "c @EtchLongVersion@ (@EtchBuildTag@)";
+}
diff --git a/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/IncludeTreeWalker.java b/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/IncludeTreeWalker.java
new file mode 100644
index 0000000..219b13e
--- /dev/null
+++ b/binding-c/compiler/src/main/java/org/apache/etch/bindings/c/compiler/IncludeTreeWalker.java
@@ -0,0 +1,184 @@
+package org.apache.etch.bindings.c.compiler;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import java.util.List;
+
+import org.apache.etch.compiler.ast.Constant;
+import org.apache.etch.compiler.ast.Enumx;
+import org.apache.etch.compiler.ast.Except;
+import org.apache.etch.compiler.ast.Extern;
+import org.apache.etch.compiler.ast.Item;
+import org.apache.etch.compiler.ast.Message;
+import org.apache.etch.compiler.ast.Mixin;
+import org.apache.etch.compiler.ast.Module;
+import org.apache.etch.compiler.ast.Parameter;
+import org.apache.etch.compiler.ast.Service;
+import org.apache.etch.compiler.ast.Struct;
+import org.apache.etch.compiler.ast.Thrown;
+import org.apache.etch.compiler.ast.TreeWalker;
+import org.apache.etch.compiler.ast.TypeRef;
+
+public class IncludeTreeWalker implements TreeWalker {
+
+ private List<String> list;
+ private Service service;
+
+ public IncludeTreeWalker(List<String> list, Service s) {
+ this.list = list;
+ this.service = s;
+ }
+
+ @Override
+ public void doExtern(Extern extern) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void doItem(Item item) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void doMixin(Mixin mixin) {
+ String s = mixin.name().name(); //.intf().name().name();
+ if (!getList().contains(s) &&
+ !s.equals(service.name().name())) {
+ getList().add(s);
+ }
+
+ }
+
+ @Override
+ public void doThrown(Thrown thrown) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void doTypeRef(TypeRef ref) {
+
+ }
+
+ @Override
+ public void postConstant(Constant constant) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void postEnum(Enumx enumx) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void postExcept(Except except) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void postMessage(Message message) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void postModule(Module module) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void postParameter(Parameter parameter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void postService(Service service) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void postStruct(Struct struct) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void preConstant(Constant constant) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void preEnum(Enumx enumx) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void preExcept(Except except) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void preMessage(Message message) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void preModule(Module module) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void preParameter(Parameter parameter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void preService(Service service) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void preStruct(Struct struct) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public List<String> getList() {
+ return list;
+ }
+
+}
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/base_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/base_c.vm
new file mode 100644
index 0000000..b765d2c
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/base_c.vm
@@ -0,0 +1,248 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+\#include "$helper.getBaseFileNameH($intf, $suffix)"
+\#include "etch_objecttypes.h"
+\#include "etch_general.h"
+\#include "etch_url.h"
+
+unsigned short CLASSID_$helper.getBaseName( $intf, $suffix ).toUpperCase()_BASE;
+
+#foreach($serviceName in $helper.getUsedServiceNames($intf))
+\#include "${serviceName.toLowerCase()}.h"
+#end
+
+#if ($helper.isServer($mc))
+int destroy_$helper.getBaseName($intf, $suffix)_via_base(void*);
+#end
+#if ($helper.isClient($mc))
+int destroy_$helper.getBaseName($intf, $suffix)_base(void*);
+#end
+
+#if ($helper.isServer($mc))
+static int $helper.getBaseName( $intf, $suffix )_id_farm;
+#end
+
+/* - - - - - - - - - - - - - -
+ * constructors
+ * - - - - - - - - - - - - - -
+ */
+## server base
+#if ($helper.isServer($mc))
+/**
+ * new_$helper.getBaseName( $intf, $suffix )_base()
+ * @param implobj not interpreted
+ * @param psi a $helper.getIntfName( $intf ) service interface. if supplied, caller retains,
+ * otherwise a service interface is instantiated and owned here.
+ */
+i_$helper.getBaseName( $intf, $suffix )* new_$helper.getBaseName( $intf, $suffix )_base(void* implobj, i_$helper.getIntfName( $intf )* psi)
+{
+ i_$helper.getBaseName( $intf, $suffix )* ips = (i_$helper.getBaseName( $intf, $suffix )*) new_object (sizeof(i_$helper.getBaseName( $intf, $suffix )),
+ ETCHTYPEB_EXESERVERBASE, get_dynamic_classid_unique(&CLASSID_$helper.getBaseName( $intf, $suffix ).toUpperCase()_BASE));
+
+ /* the server impl is destroyed via this base object. the virtual destructor we assign
+ * here will call the impl object's virtual destructor, which will directly call a
+ * non-virtual destructor for the base object.
+ */
+
+ ((etch_object*)ips)->destroy = destroy_$helper.getBaseName( $intf, $suffix )_via_base;
+
+ ips->thisx = implobj; /* null passed thru from client main */
+
+ if (psi)
+ ips->i$helper.getIntfName( $intf ) = psi;
+ else
+ { ips->i$helper.getIntfName( $intf ) = new_$helper.getIntfName( $intf )_service_interface();
+ ips->is_service_interface_owned = TRUE;
+ }
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+ ips->$n.name() = ips->i$helper.getIntfName( $intf )->$n.name();
+#end
+#end
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct())
+#if (!$n.isHidden())
+ ips->$n.name().toString() = ips->i$helper.getIntfName($intf)->$n.name().toString();
+#end
+#end
+#end
+
+ ips->iobjsession = new_default_objsession_interface (ips);
+ ips->_session_control = ips->iobjsession->_session_control;
+ ips->_session_notify = ips->iobjsession->_session_notify;
+ ips->_session_query = ips->iobjsession->_session_query;
+
+ ips->server_id = ++$helper.getIntfName( $intf )_server_id_farm;
+
+ return ips;
+}
+
+/**
+ * new_$helper.getRemoteName( $intf, $suffix )_base()
+ * constructor for server base when host is a remote server.
+ * the server base destructor in this case destroys only itself.
+ * @param psi a $helper.getIntfName( $intf ) service interface, if supplied, caller retains.
+ * may be null.
+ */
+i_$helper.getBaseName( $intf, $suffix )* new_$helper.getRemoteName( $intf, $suffix )_base (void* implobj, i_$helper.getIntfName( $intf )* psi)
+{
+ i_$helper.getBaseName( $intf, $suffix )* ips = new_$helper.getBaseName( $intf, $suffix )_base (implobj, psi);
+ ((etch_object*)ips)->destroy = destroy_$helper.getBaseName( $intf, $suffix )_base;
+ return ips;
+}
+
+/**
+ * destroy_$helper.getBaseName($intf, $suffix)_base()
+ * i_$helper.getBaseName($intf, $suffix) destructor.
+ */
+int destroy_$helper.getBaseName( $intf, $suffix )_base (void* data)
+{
+
+ i_$helper.getBaseName( $intf, $suffix )* ips = (i_$helper.getBaseName( $intf, $suffix )*)data;
+ if (NULL == ips) return -1;
+
+ if (!is_etchobj_static_content(ips))
+ {
+ if (ips->is_service_interface_owned){
+ //ETCHOBJ_DESTROY(ips->i$helper.getIntfName( $intf ));
+ if(ips->i$helper.getIntfName( $intf )){
+ etch_object_destroy(ips->i$helper.getIntfName( $intf ));
+ }
+ ips->i$helper.getIntfName( $intf ) = NULL;
+
+ }
+
+ etch_free(ips->iobjsession);
+ }
+
+ return destroy_objectex((etch_object*)ips);
+}
+
+
+/**
+ * destroy_$helper.getBaseName( $intf, $suffix )_via_base()
+ * destructor for $helper.getBaseName( $intf, $suffix )_impl via i_$helper.getBaseName( $intf, $suffix ).
+ */
+int destroy_$helper.getBaseName( $intf, $suffix )_via_base (void* data)
+{
+ i_$helper.getBaseName( $intf, $suffix )* ips = (i_$helper.getBaseName( $intf, $suffix )*)data;
+ if (NULL == ips) return -1;
+
+ if (!is_etchobj_static_content(ips))
+ {
+ /* serverimpl dtor will call base dtor (destroy_$helper.getBaseName( $intf, $suffix )_base) */
+ etch_object* serverimpl = (etch_object*) ips->thisx;
+ ETCH_ASSERT(is_etch_serverimpl(serverimpl));
+ //ETCHOBJ_DESTROY(serverimpl);
+ if(serverimpl){
+ etch_object_destroy(serverimpl);
+ }
+ serverimpl = NULL;
+ }
+
+ return 0;
+}
+
+#end
+## client base
+#if ($helper.isClient($mc))
+/**
+ * new_$helper.getBaseName($intf, $suffix)_base()
+ * @param iservice service interface -- caller retains
+ */
+i_$helper.getBaseName($intf, $suffix)* new_$helper.getBaseName($intf, $suffix)_base(struct $helper.getImplName($intf, $suffix)* implobj)
+{
+ i_$helper.getBaseName( $intf, $suffix )* ipc = (i_$helper.getBaseName( $intf, $suffix )*) new_object (sizeof(i_$helper.getBaseName( $intf, $suffix )),
+ ETCHTYPEB_EXECLIENTBASE, get_dynamic_classid_unique(&CLASSID_$helper.getBaseName( $intf, $suffix ).toUpperCase()_BASE));
+
+ ipc->thisx = implobj; /* $helper.getImplName( $intf, $suffix ) on client, null on server */
+ ((etch_object*)ipc)->destroy = destroy_$helper.getBaseName( $intf, $suffix )_base;
+
+ ipc->i$helper.getIntfName( $intf ) = new_$helper.getIntfName( $intf )_service_interface();
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+ ipc->$n.name() = ipc->i$helper.getIntfName($intf)->$n.name();
+#end
+#end
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct())
+#if (!$n.isHidden())
+ ipc->$n.name().toString() = ipc->i$helper.getIntfName($intf)->$n.name().toString();
+#end
+#end
+#end
+
+ return ipc;
+}
+
+/**
+ * destroy_$helper.getBaseName( $intf, $suffix )_base()
+ * i_$helper.getBaseName( $intf, $suffix ) destructor.
+ */
+int destroy_$helper.getBaseName( $intf, $suffix )_base (void* data)
+{
+ i_$helper.getBaseName( $intf, $suffix )* ipc = (i_$helper.getBaseName( $intf, $suffix )*)data;
+ if (NULL == ipc) return -1;
+
+ if (!is_etchobj_static_content(ipc))
+ {
+ if (ipc->thisx) /* thisx is null on server (i.e. this is a remote client) */
+ { /* destroy the $helper.getBaseName( $intf, $suffix )_impl object */
+ ETCH_ASSERT(is_etch_client_impl((etch_object*)ipc->thisx));
+ //ETCHOBJ_DESTROY();
+ if(((etch_object*)ipc->thisx)){
+ etch_object_destroy(((etch_object*)ipc->thisx));
+ }
+ ipc->thisx = NULL;
+ }
+
+ //ETCHOBJ_DESTROY(ipc->i$helper.getIntfName( $intf ));
+ if(ipc->i$helper.getIntfName( $intf )){
+ etch_object_destroy(ipc->i$helper.getIntfName( $intf ));
+ }
+ ipc->i$helper.getIntfName( $intf ) = NULL;
+
+ etch_free(ipc->iobjsession);
+ }
+
+ return destroy_objectex((etch_object*)ipc);
+}
+
+/* - - - - - - - - - - - - - -
+ * client base methods
+ * - - - - - - - - - - - - - -
+ */
+
+/* nothing to do - service defines no client-directed items */
+
+#end
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/base_h.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/base_h.vm
new file mode 100644
index 0000000..9960c76
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/base_h.vm
@@ -0,0 +1,133 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getBaseFileNameH( $intf, $suffix )
+ * $helper.getServiceName( $intf ) client interface.
+ * combines java bindings's $intf.name()Server and Base$intf.name()Server
+ */
+
+#ifndef $helper.getBaseName( $intf, $suffix ).toUpperCase()_H
+#define $helper.getBaseName( $intf, $suffix ).toUpperCase()_H
+
+\#include "$helper.getIntfFileNameH( $intf )"
+\#include "etch_sessionint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_$helper.getBaseName( $intf, $suffix ).toUpperCase()_BASE;
+
+//typedef struct $helper.getImplName( $intf, $suffix ) $helper.getImplName( $intf, $suffix );
+
+/**
+ * i_$helper.getBaseName( $intf, $suffix )
+ * $helper.getServiceName( $intf ) $suffix base interface
+ */
+typedef struct i_$helper.getBaseName( $intf, $suffix )
+{
+ etch_object object;
+
+ struct $helper.getImplName( $intf, $suffix )* thisx;
+ i_$helper.getServiceName( $intf )* i$helper.getServiceName( $intf );
+
+
+#if ($helper.isServer($mc))
+ int session_id;
+ unsigned char is_service_interface_owned;
+ unsigned char unused[3];
+#end
+
+ /* - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - -
+ */
+ i_objsession* iobjsession;
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - -
+ * service virtuals
+ * - - - - - - - - - - -
+ */
+## generate service virtuals
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+ $helper.getServiceName( $intf )_$n.name() $n.name();
+#end
+#end
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+#if (!$n.isHidden())
+ $helper.getServiceName( $intf )_$n.name() async_$n.name();
+#end
+#end
+#end
+#end
+#if (!$intf.hasMessageDirection($mc) && $helper.hasMessageDirectionBoth( $intf ))
+ /* no $mc.toString().toLowerCase()-directed items defined */
+#end
+
+ /* - - - - - - - - - - -
+ * service data
+ * - - - - - - - - - - -
+ */
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct())
+#if (!$n.isHidden())
+ $n.efqname($helper)* $n.name().toString();
+#end
+#end
+#end
+
+ /* - - - - - - - - - - -
+ * private instance data
+ * - - - - - - - - - - -
+ */
+ int server_id;
+
+} i_$helper.getBaseName($intf, $suffix);
+
+#if($helper.isServer($mc))
+i_$helper.getBaseName( $intf, $suffix )* new_$helper.getBaseName( $intf, $suffix )_base (void* implobj, i_$helper.getIntfName( $intf )*);
+i_$helper.getBaseName( $intf, $suffix )* new_$helper.getRemoteName( $intf, $suffix )_base (void* implobj, i_$helper.getIntfName( $intf )*);
+int destroy_$helper.getBaseName( $intf, $suffix )_base (void*);
+#end
+#if($helper.isClient( $mc ))
+//i_$helper.getBaseName($intf, $suffix)* new_$helper.getBaseName($intf, $suffix)_base($helper.getImplName($intf, $suffix)* implobj);
+i_$helper.getBaseName($intf, $suffix)* new_$helper.getBaseName($intf, $suffix)_base();
+#end
+
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* $helper.getBaseName( $intf, $suffix ).toUpperCase()_H */
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/cKeywords.kwd b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/cKeywords.kwd
new file mode 100644
index 0000000..cfc9621
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/cKeywords.kwd
@@ -0,0 +1,35 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+true
+false
+null
+for
+switch
+assert
+default
+goto
+bool
+do
+if
+break
+double
+char
+else
+case
+enum
+return
+catch
+int
+short
+static
+void
+struct
+long
+volatile
+const
+float
+native
+while
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/etch_keywords.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/etch_keywords.vm
new file mode 100644
index 0000000..9287887
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/etch_keywords.vm
@@ -0,0 +1,64 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+#foreach($n in $intf.iterator())
+#if($n.isMessage())
+#if(!$n.isHidden())
+#set ( $str="$intf.fqname().$n.name()" )
+$helper.getKeywordForWireshark($str)
+#if(! $n.isOneway())
+#set ( $str="$intf.fqname()._result_$n.name()" )
+$helper.getKeywordForWireshark($str)
+#end
+#end
+#end
+#end
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+$helper.getKeywordForWireshark($n.fqname().toString())
+#end
+#end
+#set ($tmp = $helper.resetHistory())
+#foreach($n in $intf.iterator())
+#if(!$n.isHidden())
+#if(!$n.isBuiltin())
+#foreach($p in $n.iterator())
+#if(!$helper.historyContains($p.name().toString()))
+$helper.getKeywordForWireshark($p.name().toString())
+#set ($tmp = $helper.addStringToHistory($p.name().toString()))
+#end
+#end
+#end
+#end
+#end
+$helper.getKeywordForWireshark("_Etch_RuntimeException")
+$helper.getKeywordForWireshark("_Etch_AuthException")
+$helper.getKeywordForWireshark("_exception")
+$helper.getKeywordForWireshark("_Etch_List")
+$helper.getKeywordForWireshark("_Etch_Map")
+$helper.getKeywordForWireshark("_Etch_Set")
+$helper.getKeywordForWireshark("_Etch_Datetime")
+$helper.getKeywordForWireshark("msg")
+$helper.getKeywordForWireshark("_messageId")
+$helper.getKeywordForWireshark("_inReplyTo")
+$helper.getKeywordForWireshark("result")
+$helper.getKeywordForWireshark("keys")
+$helper.getKeywordForWireshark("values")
+$helper.getKeywordForWireshark("dateTime")
+$helper.getKeywordForWireshark("keysAndValues")
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/helper_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/helper_c.vm
new file mode 100644
index 0000000..c967a0d
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/helper_c.vm
@@ -0,0 +1,342 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#set($i = $intf.name().name().toLowerCase())
+
+/*
+ * ${i}_helper.c
+ * transport helper for ${i} service
+ */
+\#include "$helper.getIntfFileNameH($intf)"
+
+#if($helper.isServer($mc))
+\#include "${i}_server.h"
+\#include "${i}_server_stub.h"
+\#include "${i}_remote_client.h"
+#end
+#if($helper.isClient($mc))
+\#include "${i}_client_stub.h"
+\#include "${i}_remote_server.h"
+#end
+
+\#include "${i}_helper.h"
+\#include "${i}_valufact.h"
+\#include "etch_svcobj_masks.h"
+\#include "etch_objecttypes.h"
+\#include "etch_url.h"
+\#include "etch_log.h"
+
+static const char* LOG_CATEGORY = "$helper.getServiceName($intf)_helper";
+
+static int ${i}_helper_resources_init(void* data)
+{
+ etch_server_factory* factory = (etch_server_factory*)data;
+ int result = 0;
+ ETCH_ASSERT((factory != NULL) && (factory->in_valufact == NULL));
+ ETCH_ASSERT (factory->in_resx && is_etch_hashtable(factory->in_resx));
+
+ // TODO use new semantic ${i}_valuefactory_create
+ factory->in_valufact = (etch_value_factory*)new_${i}_valufact();
+ ETCH_ASSERT(factory->in_valufact);
+ if(factory->in_valufact == NULL) {
+ return -1;
+ }
+ result = etch_resources_add(factory->in_resx, ETCH_RESXKEY_MSGIZER_VALUFACT, (etch_object*)factory->in_valufact);
+
+ ETCH_ASSERT(0 == result);
+ return result;
+}
+
+#if($helper.isClient($mc))
+etch_status_t ${i}_helper_remote_server_create(${i}_remote_server** remote_server, wchar_t* uri, void* factory_thisx, main_client_create_func client_create)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_client_factory* factory = NULL;
+ ${i}_remote_server* newremote_server = NULL;
+ i_${i}_client* client = NULL;
+ ${i}_client_stub* client_stub = NULL;
+ ${i}_valufact* vf = NULL;
+
+ if(remote_server == NULL || client_create == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "creating ${i} client ...\n");
+ factory = new_client_factory (NULL, NULL, client_create);
+ ETCH_ASSERT(factory != NULL);
+
+ factory->thisx = factory_thisx;
+
+ vf = new_${i}_valufact();
+ ETCH_ASSERT(vf != NULL);
+
+ factory->in_valufact = (etch_value_factory*)vf;
+ factory->in_resx = etch_transport_resources_init(factory->in_resx);
+ ETCH_ASSERT(factory->in_resx != NULL);
+ etch_resources_add (factory->in_resx, ETCH_RESXKEY_MSGIZER_VALUFACT, (etch_object*)vf);
+
+ // instantiate a delivery service
+ factory->dsvc = new_etch_transport(uri, (etch_factory_params*)factory, NULL);
+ ETCH_ASSERT(is_etch_ideliverysvc(factory->dsvc));
+
+ // instantiate the remote server
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "instantiating remote server ...\n");
+ newremote_server = new_${i}_remote_server(NULL, factory->dsvc, (etch_value_factory*)vf);
+ ETCH_ASSERT(is_etch_remote_server(newremote_server));
+
+ factory->server_id = newremote_server->server_base->server_id;
+ factory->server = newremote_server;
+ newremote_server->client_factory = factory;
+
+ /* here we call back to the client constructor in [main]. the purpose of the
+ * callback is to isolate the editable xxxx_client_impl constructor from the
+ * private constructor pieces. the callback instantiates a client implenentation
+ * and returns an interface to it.
+ */
+ if(factory->new_client != NULL) {
+ client = factory->new_client(factory, newremote_server);
+ ETCH_ASSERT(is_etch_client_base(client));
+ factory->iclient = client;
+ }
+
+ // get thread pools
+ factory->fpool = (etch_threadpool*)etch_resources_get(factory->in_resx, ETCH_RESXKEY_POOLTYPE_FREE);
+ ETCH_ASSERT(factory->fpool);
+ factory->qpool = (etch_threadpool*)etch_resources_get(factory->in_resx, ETCH_RESXKEY_POOLTYPE_QUEUED);
+ ETCH_ASSERT(factory->qpool);
+
+ // construct client stub
+ client_stub = new_${i}_client_stub(factory);
+ ETCH_ASSERT(is_etch_client_stub(client_stub));
+ factory->stub = client_stub;
+
+ *remote_server = newremote_server;
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG,"remote server instantiated\n");
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "${i} client created\n");
+
+ return rv;
+}
+
+etch_status_t ${i}_helper_remote_server_start_wait(${i}_remote_server* remote_server, const int waitms)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ ${i}_remote* remote = NULL;
+ int result = 0;
+
+ if(remote_server == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ remote = remote_server->remote_base;
+ ETCH_ASSERT(remote != NULL);
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "starting ${i} client ...\n");
+ result = remote->start_waitup(remote, waitms);
+ if(result != 0) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not start ${i} client\n");
+ rv = ETCH_ERROR;
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "${i} client started\n");
+ rv = ETCH_SUCCESS;
+ }
+
+ return rv;
+}
+
+etch_status_t ${i}_helper_remote_server_stop_wait(${i}_remote_server* remote_server, const int waitms)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ ${i}_remote* remote = NULL;
+ int result = 0;
+
+ if(remote_server == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ remote = remote_server->remote_base;
+ ETCH_ASSERT(remote != NULL);
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "stopping ${i} client ...\n");
+ result = remote->stop_waitdown(remote, waitms);
+ if(result != 0) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not stop ${i} client\n");
+ rv = ETCH_ERROR;
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "${i} client stopped\n");
+ rv = ETCH_SUCCESS;
+ }
+
+ return rv;
+}
+
+etch_status_t ${i}_helper_remote_server_destroy(${i}_remote_server* remote_server)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+
+ if(remote_server == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying remote server ...\n");
+ etch_object_destroy(remote_server);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "remote server destroyed\n");
+
+ return rv;
+}
+
+#end
+#if($helper.isServer($mc))
+static void* ${i}_helper_listener_create_func(void* factoryData, void* sessionData)
+{
+ etch_server_factory* factory = (etch_server_factory*)factoryData;
+ etch_session* session = (etch_session*)sessionData;
+ i_${i}_server* iserver;
+ ${i}_server_stub* stub;
+ ${i}_remote_client* client;
+ ETCH_ASSERT(factory && factory->helper_new_listener && factory->main_new_server);
+ ETCH_ASSERT(factory->in_resx && factory->in_valufact); // TODO assert delivery service
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "instantiating accepted client listener ...\n");
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "creating remote client...\n");
+ client = new_${i}_remote_client(NULL, session, factory->in_valufact);
+ client->session_id = session->session_id;
+ session->client = client;
+
+ /* here we CALL BACK to the constructor in [main], the purpose of the callback
+ * being to isolate the editable constructor from the private constructor.
+ * the callback instantiates a client's server implementation and returns
+ * an interface to it.
+ */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "creating server implementation ...\n");
+ iserver = factory->main_new_server(factory, session);
+ iserver->session_id = session->session_id;
+ session->server = iserver;
+
+ /* note that the main listener will use p->mainpool as a thread manager, not these */
+ factory->qpool = (etch_threadpool*)etch_resources_get(factory->in_resx, ETCH_RESXKEY_POOLTYPE_QUEUED);
+ factory->fpool = (etch_threadpool*)etch_resources_get(factory->in_resx, ETCH_RESXKEY_POOLTYPE_FREE);
+
+ /* eventually new_${i}_server_stub() gets to stub_base constructor, which sets
+ * the delivery service's session to this, the server stub. so, in the java binding,
+ * the server stub is referenced as delivery service.session. we should perhaps also
+ * store the stub opaquely in both the client and listener objects.
+ */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "creating server stub ...\n");
+ stub = new_${i}_server_stub(factory, session);
+ stub->session_id = session->session_id;
+ session->server_stub = stub;
+
+ if (iserver && stub)
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "accepted client listener instantiated\n");
+ else
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not instantiate accepted client listener\n");
+
+ return stub;
+}
+
+etch_status_t ${i}_helper_listener_create(i_sessionlistener** listener, wchar_t* uri, void* factory_thisx, main_server_create_func server_create)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ i_sessionlistener* newlistener = NULL;
+
+ if(listener == NULL || server_create == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "instantiating main listener ...\n");
+
+ newlistener = new_etch_listener(uri, NULL, factory_thisx, ${i}_helper_listener_create_func, server_create, ${i}_helper_resources_init);
+ if(newlistener == NULL) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not instantiate main listener\n");
+ rv = ETCH_ERROR;
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "main listener instantiated\n");
+ *listener = newlistener;
+ rv = ETCH_SUCCESS;
+ }
+
+ return rv;
+}
+
+etch_status_t ${i}_helper_listener_start_wait(i_sessionlistener* listener, const int waitms)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ int result = 0;
+
+ if(listener == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "starting main listener ...\n");
+
+ result = listener->transport_control(listener->thisx, new_etch_event(CLASSID_CONTROL_START_WAITUP, waitms), NULL);
+ if(result != 0) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not start main listener\n");
+ rv = ETCH_ERROR;
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "main listener started on thread %d\n", transport_thread_id(listener));
+ }
+
+ return rv;
+}
+
+etch_status_t ${i}_helper_listener_stop_wait(i_sessionlistener* listener, const int waitms)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ int result = 0;
+
+ if(listener == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "stopping main listener ...\n");
+
+ result = listener->transport_control(listener->thisx, new_etch_event(CLASSID_CONTROL_STOP_WAITDOWN, waitms), NULL);
+ if(result != 0) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not stop main listener\n");
+ rv = ETCH_ERROR;
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "main listener ended\n");
+
+ if (transport_session_count (listener) > 0) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "begin client sessions teardown\n");
+ result = transport_teardown_client_sessions(listener);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "end client sessions teardown\n");
+ }
+ }
+
+ return rv;
+}
+
+etch_status_t ${i}_helper_listener_destroy(i_sessionlistener* listener)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+
+ etch_object_destroy(listener);
+
+ return rv;
+}
+
+#end //isSERVER
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/helper_h.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/helper_h.vm
new file mode 100644
index 0000000..ffa2dc6
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/helper_h.vm
@@ -0,0 +1,60 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#set($i = $intf.name().name().toLowerCase())
+
+#ifndef $i.toUpperCase()_HELPER_H
+#define $i.toUpperCase()_HELPER_H
+
+\#include "etch.h"
+\#include "etch_errno.h"
+\#include "etch_transport.h"
+#if($helper.isClient($mc))
+\#include "${i}_remote_server.h"
+#end
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if($helper.isClient($mc))
+etch_status_t $helper.getServiceName($intf)_helper_remote_server_create($helper.getServiceName($intf)_remote_server** remote_server, wchar_t* uri, void* factory_thisx, main_client_create_func client_create);
+etch_status_t $helper.getServiceName($intf)_helper_remote_server_start_wait($helper.getServiceName($intf)_remote_server* remote_server, const int waitms);
+etch_status_t $helper.getServiceName($intf)_helper_remote_server_stop_wait($helper.getServiceName($intf)_remote_server* remote_server, const int waitms);
+etch_status_t $helper.getServiceName($intf)_helper_remote_server_destroy($helper.getServiceName($intf)_remote_server* remote_server);
+#end
+
+#if($helper.isServer($mc))
+etch_status_t $helper.getServiceName($intf)_helper_listener_create(i_sessionlistener** listener, wchar_t* uri, void* factory_thisx, main_server_create_func);
+etch_status_t $helper.getServiceName($intf)_helper_listener_start_wait(i_sessionlistener* listener, const int waitms);
+etch_status_t $helper.getServiceName($intf)_helper_listener_stop_wait(i_sessionlistener* listener, const int waitms);
+etch_status_t $helper.getServiceName($intf)_helper_listener_destroy(i_sessionlistener* listener);
+#end
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* $i.toUpperCase()_HELPER_H */
+
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/impl_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/impl_c.vm
new file mode 100644
index 0000000..181a992
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/impl_c.vm
@@ -0,0 +1,107 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+\#include "$helper.getImplFileNameH($intf, $suffix)"
+\#include "etch_url.h"
+\#include "etch_arrayval.h"
+\#include "etch_binary_tdo.h"
+\#include "etch_exception.h"
+\#include "etch_general.h"
+\#include "etch_log.h"
+
+\#include <stdio.h>
+
+unsigned short CLASSID_$helper.getImplName($intf, $suffix).toUpperCase();
+
+#foreach($serviceName in $helper.getUsedServiceNames($intf))
+\#include "${serviceName.toLowerCase()}.h"
+#end
+
+#if($helper.isServer($mc))
+char* $helper.getServiceName($intf).toUpperCase()_ETCHSIMP = "SIMP";
+#end
+#if($helper.isClient($mc))
+char* $helper.getServiceName($intf).toUpperCase()_ETCHCIMP = "CIMP";
+#end
+
+/* generated signatures */
+int destroy_${helper.getImplName($intf, $suffix)}x(void*);
+$helper.getImplName($intf, $suffix)* init_$helper.getImplName($intf, $suffix)(struct $helper.getRemoteName($intf, $helper.getRemoteDirectionName($mc))*, etch_object_destructor);
+
+## check if any Message goes to $suffix direction
+
+/* - - - - - - - -
+ * instantiation
+ * - - - - - - - -
+ */
+
+/**
+ * new_$helper.getImplName($intf, $suffix)()
+ * $helper.getImplName($intf, $suffix) constructor.
+ * add your custom initialization and virtual method overrides here.
+ */
+$helper.getImplName($intf, $suffix)* new_$helper.getImplName($intf, $suffix)(struct $helper.getRemoteName($intf, $helper.getRemoteDirectionName($mc))* $helper.getRemoteDirectionName($mc))
+{
+ $helper.getImplName($intf, $suffix)* p$suffix /* allocate object and assign default virtuals */
+ = init_$helper.getImplName($intf, $suffix)($helper.getRemoteDirectionName($mc), destroy_${helper.getImplName($intf, $suffix)}x);
+#if($helper.isServer($mc))
+ i_$helper.getBaseName($intf, $suffix)* p${suffix}_base = p${suffix}->$helper.getBaseName($intf, $suffix)_base;
+
+ ((etch_object*)p${suffix}_base)->class_id = get_dynamic_classid_unique(&CLASSID_$helper.getImplName($intf, $suffix).toUpperCase());
+#end
+ /* add virtual method overrides, if any, here */
+ //p${suffix}->xxx = implementation
+
+ return p$suffix;
+}
+
+
+/**
+ * destroy_${helper.getImplName($intf, $suffix)}x()
+ * destructor for any user allocated memory.
+ * this code is invoked by the private perf_client_impl destructor,
+ * via perf_client.destroyex(). add code here to destroy any memory you
+ * may have allocated for your custom perf_client implementation.
+ */
+int destroy_${helper.getImplName($intf, $suffix)}x(void* data)
+{
+ /*
+ $helper.getImplName($intf, $suffix)* thisx = ($helper.getImplName($intf, $suffix)*)data;
+ */
+ /* * * add custom destruction here * * */
+ /* etch_free(thisx->exampleobj); */
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - -
+ * session interface method overrides
+ * - - - - - - - - - - - - - - - - - - -
+ */
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * implementations of $helper.getBaseName($intf, $suffix) messages from server, if any
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/impl_h.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/impl_h.vm
new file mode 100644
index 0000000..5b13120
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/impl_h.vm
@@ -0,0 +1,89 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#ifndef $helper.getImplName($intf, $suffix).toUpperCase()_H
+#define $helper.getImplName($intf, $suffix).toUpperCase()_H
+
+\#include "$helper.getBaseFileNameH($intf, $suffix)"
+\#include "etch_transport.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_$helper.getImplName($intf, $suffix).toUpperCase();
+
+//typedef struct $helper.getRemoteName($intf, $helper.getDirectionName($helper.getRemoteDirection($mc))) $helper.getRemoteName($intf, $helper.getDirectionName($helper.getRemoteDirection($mc)));
+
+/**
+ * $helper.getImplName($intf, $suffix)
+ * your custom implementation of $helper.getBaseName($intf, $suffix). add methods here
+ * to provide implementations of messages from the client, if any.
+ */
+typedef struct $helper.getImplName($intf, $suffix)
+{
+ etch_object object;
+
+ i_$helper.getBaseName($intf, $suffix)* $helper.getBaseName($intf, $suffix)_base; /* owned */
+ i_$intf.name().name().toLowerCase()* i$intf.name().name().toLowerCase(); /* not owned */
+ struct $helper.getRemoteName($intf, $helper.getDirectionName($helper.getRemoteDirection($mc)))* $helper.getDirectionName($helper.getRemoteDirection($mc)); /* not owned */
+
+ int (*destroyex) (void*); /* user memory destructor */
+
+ /* - - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - - -
+ */
+ i_objsession* iobjsession; /* owned by base */
+ /* note that iobjsession->thisx is set to this $helper.getImplName($intf, $suffix)* */
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - - -
+ * base service virtuals
+ * - - - - - - - - - - - -
+ */
+## generate service virtuals
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+ $helper.getServiceName($intf).toLowerCase()_$n.name() $n.name();
+#end
+#end
+#end
+
+ void* context;
+
+
+} $helper.getImplName($intf, $suffix);
+
+/* constructor */
+$helper.getImplName($intf, $suffix)* new_$helper.getImplName($intf, $suffix) (struct $helper.getRemoteName($intf, $helper.getDirectionName($helper.getRemoteDirection($mc)))*);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* $helper.getImplName($intf, $suffix).toUpperCase()_H */
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/implx_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/implx_c.vm
new file mode 100644
index 0000000..b368d5f
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/implx_c.vm
@@ -0,0 +1,128 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getImplXFileNameI($intf, $suffix)
+ * $helper.getImplName functions which would ordinarily not be subject to edit.
+ */
+
+\#include "$helper.getImplFileNameH($intf, $suffix)"
+\#include "$helper.getRemoteFileNameH($intf, $helper.getRemoteDirectionName($mc))"
+\#include "etch_objecttypes.h"
+\#include "etch_general.h"
+\#include "etch_url.h"
+
+int destroy_$helper.getImplName($intf, $suffix)(void*);
+
+/* - - - - - - - - - - - - - - - - - - - - - - - -
+ *$helper.getImplName($intf, $suffix) private construction / destruction
+ * - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * init_$helper.getImplName($intf, $suffix)()
+ * called by $helper.getImplName($intf, $suffix) constructor to instantiate server implementation
+ * object and initialize with default virtuals.
+ * @param client the remote client, not owned.
+ * @param usermem_dtor destructor for any custom memory allocations.
+ */
+$helper.getImplName($intf, $suffix)* init_$helper.getImplName($intf, $suffix)($helper.getRemoteName($intf, $helper.getRemoteDirectionName($mc))* $helper.getRemoteDirectionName($mc),
+ etch_object_destructor usermem_dtor)
+{
+#if($helper.isServer($mc))
+ $helper.getImplName($intf, $suffix)* p${suffix} = ($helper.getImplName($intf, $suffix)*) new_object (sizeof($helper.getImplName($intf, $suffix)),
+ ETCHTYPEB_EXESERVERIMPL, get_dynamic_classid_unique(&CLASSID_$helper.getImplName($intf, $suffix).toUpperCase()));
+#end
+#if ($helper.isClient($mc))
+ $helper.getImplName($intf, $suffix)* p${suffix} = ($helper.getImplName($intf, $suffix)*) new_object (sizeof($helper.getImplName($intf, $suffix)),
+ ETCHTYPEB_EXECLIENTIMPL, get_dynamic_classid_unique(&CLASSID_$helper.getImplName($intf, $suffix).toUpperCase()));
+#end
+
+ p${suffix}->$helper.getRemoteDirectionName($mc) = $helper.getRemoteDirectionName($mc); /* not owned */
+ ((etch_object*)p${suffix})->destroy = destroy_$helper.getImplName($intf, $suffix); /* private destructor */
+ p${suffix}->destroyex = usermem_dtor; /* user memory destructor */
+
+ /* instantiate base and copy virtuals, if any, to this object */
+#if ($helper.isServer($mc))
+ p${suffix}->$helper.getBaseName($intf, $suffix)_base = new_$helper.getBaseName($intf, $suffix)_base(p${suffix}, NULL); /* owned */
+#end
+#if ($helper.isClient($mc))
+ p${suffix}->$helper.getBaseName($intf, $suffix)_base = new_$helper.getBaseName($intf, $suffix)_base(p${suffix}); /* owned */
+#end
+
+ p${suffix}->i$helper.getIntfName($intf) = p${suffix}->$helper.getBaseName($intf, $suffix)_base->i$helper.getIntfName($intf);
+
+
+#foreach($n in $intf.iterator())
+#if($n.isMessage())
+#if(!$n.isHidden())
+#if($n.isMsgDir($mc) || $n.isMsgDirBoth())
+ p${suffix}->$n.name() = p${suffix}->$helper.getBaseName($intf, $suffix)_base->$n.name();
+#end
+#end
+#end
+#end
+
+
+#if ($helper.isServer($mc))
+ p${suffix}->iobjsession = p${suffix}->$helper.getBaseName($intf, $suffix)_base->iobjsession;
+#end
+#if ($helper.isClient($mc))
+ p${suffix}->iobjsession = $helper.getRemoteDirectionName($mc)->server_base->iobjsession;
+#end
+ p${suffix}->iobjsession->thisx = p${suffix}; /* set implementor reference */
+ p${suffix}->_session_control = p${suffix}->$helper.getBaseName($intf, $suffix)_base->_session_control;
+ p${suffix}->_session_notify = p${suffix}->$helper.getBaseName($intf, $suffix)_base->_session_notify;
+ p${suffix}->_session_query = p${suffix}->$helper.getBaseName($intf, $suffix)_base->_session_query;
+
+ return p${suffix};
+}
+
+/**
+ * destroy_perf_server_impl()
+ * perf_server_impl private destructor.
+ * calls back to user destructor to effect cleanup of any perf_server_impl
+ * memory which may have been allocated in custom code added by user.
+ */
+int destroy_$helper.getImplName($intf, $suffix) (void* data)
+{
+ $helper.getImplName($intf, $suffix)* thisx = ($helper.getImplName($intf, $suffix)*)data;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ if(thisx->destroyex)
+ { /* call back to user memory destructor */
+ thisx->destroyex(thisx);
+ }
+#if ($helper.isServer($mc))
+ if(thisx->$helper.getBaseName($intf, $suffix)_base)
+ {
+ destroy_$helper.getBaseName($intf, $suffix)_base(thisx->$helper.getBaseName($intf, $suffix)_base);
+ }
+#end
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/intf_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/intf_c.vm
new file mode 100644
index 0000000..577c53d
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/intf_c.vm
@@ -0,0 +1,296 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+\#include "$helper.getIntfFileNameH($intf)"
+\#include "etch_url.h"
+\#include "etch_objecttypes.h"
+\#include "etch_general.h"
+\#include "etch_cache.h"
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isConstant())
+$helper.getNativeTypeNameForConstants( $n.type() ) $n.name() = $helper.getTypeValue( $n.type(), $n.value() );
+#end
+#end
+
+
+## generate CLASSIDs
+unsigned short CLASSID_$helper.getIntfName( $intf ).toUpperCase()_SERVICE_INTERFACE;
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+#if (!$hasBaseClass)
+unsigned short CLASSID_$n.efqname($helper).toUpperCase();
+#if ($n.hasExtends())
+unsigned short CLASSID_$n.efqname($helper).toUpperCase()_VTABLE;
+#end
+#end
+#end
+#end
+
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getServiceName($intf)_def_$n.name()(void* thisx")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb);")
+$sb
+#end
+#end
+#end
+
+int destroy_$helper.getIntfName( $intf )_service_interface (void*);
+
+
+/*
+ * destructors
+ */
+## generate stucts
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+/**
+ * destroy_$n.efqname($helper)()
+ * $n.name().toString().toLowerCase() object destructor
+ */
+int destroy_$n.efqname($helper)(void* data)
+{
+ $n.efqname($helper)* this = ($n.efqname($helper)*)data;
+#if($n.isExcept())
+ if(!((etch_object*)this)->is_static)
+ etch_object_destroy(this->message);
+#end
+#foreach($p in $n.getAllParameters() )
+#if($p.type().isArray())
+ if(!((etch_object*)this)->is_static && this->$p.name())
+ etch_object_destroy(this->$p.name());
+#elseif((!$p.type().isBuiltin() && ! $helper.isEnumParam($p)) || $p.type().isString() || $p.type().isObject())
+ if(!((etch_object*)this)->is_static && this->$p.name())
+ etch_object_destroy(this->$p.name());
+#end
+#end
+ destroy_object(this);
+ return 0;
+}
+#end
+#end
+
+/* - - - - - - - - - - - - - -
+ * constructors
+ * - - - - - - - - - - - - - -
+ */
+
+## generate stucts
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+#if (!$hasBaseClass)
+/**
+ * new_$n.efqname($helper)()
+ * $n.name().toString().toLowerCase() object constructor.
+ */
+$n.efqname($helper)* new_$n.efqname($helper)()
+{
+#if($n.hasExtends())
+ etchparentinfo* inheritlist = NULL;
+ vtabmask* vtab = NULL;
+#end
+#if($n.isExcept())
+
+ $n.efqname( $helper)* $n.name().toString().toLowerCase() = ($n.efqname($helper)*) new_object(sizeof($n.efqname($helper)),
+ ETCHTYPEB_EXCEPTION, get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()));
+
+ $n.name().toString().toLowerCase()->message = new_stringw(L"user generated exception, no default exception message.");
+ $n.name().toString().toLowerCase()->errorcode = ETCH_ERROR;
+ $n.name().toString().toLowerCase()->excptype = EXCPTYPE_USERDEFINED;
+
+#else
+ $n.efqname( $helper)* $n.name().toString().toLowerCase() = ($n.efqname($helper)*) new_object(sizeof($n.efqname($helper)),
+ ETCHTYPEB_USER, get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()));
+#end
+
+#if($n.hasExtends())
+ if (NULL == (vtab = etch_cache_find(get_vtable_cachehkey(get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()_VTABLE)), 0)))
+ {
+ vtab = new_vtable(NULL, sizeof(vtabmask), get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()_VTABLE));
+ etch_cache_insert(((etch_object*)vtab)->get_hashkey(vtab), vtab, FALSE);
+
+ inheritlist = get_vtab_inheritance_list((etch_object*)$n.name().toString().toLowerCase(),
+ 2, 1, get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()_VTABLE)); /* create inheritance list */
+ inheritlist[1].o.obj_type = ETCHTYPEB_USER;
+ inheritlist[1].c.class_id = get_dynamic_classid_unique(&CLASSID_$n.getExtends().efqname($helper).toUpperCase());
+ }
+ ((etch_object*)$n.name().toString().toLowerCase())->vtab = vtab;
+#end
+ ((etch_object*)$n.name().toString().toLowerCase())->destroy = destroy_$n.efqname($helper);
+
+ return $n.name().toString().toLowerCase();
+}
+
+
+#if ($n.isEnumx())
+$n.efqname($helper)* new_$n.efqname($helper)_init($n.efqname($helper)_enum val) {
+ $n.efqname($helper)* theEnum = new_$n.efqname($helper)();
+ theEnum->value = val;
+ return theEnum;
+}
+#end
+
+
+
+/**
+ * clone_$n.efqname($helper)()
+ * $n.name().toString().toLowerCase() object copy constructor.
+ */
+$n.efqname($helper)* clone_$n.efqname($helper)($n.efqname($helper)* other)
+{
+#if($n.hasExtends())
+ etchparentinfo* inheritlist = NULL;
+ vtabmask* vtab = NULL;
+#end
+#if($n.isExcept())
+ $n.efqname( $helper)* $n.name().toString().toLowerCase() = ($n.efqname($helper)*) new_object(sizeof($n.efqname($helper)),
+ ETCHTYPEB_EXCEPTION, get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()));
+#else
+ $n.efqname( $helper)* $n.name().toString().toLowerCase() = ($n.efqname($helper)*) new_object(sizeof($n.efqname($helper)),
+ ETCHTYPEB_USER, get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()));
+#end
+
+#foreach($p in $n.getAllParameters())
+ $n.name().toString().toLowerCase()->$p.name() = other->$p.name();
+#end
+
+#if($n.hasExtends())
+ if (NULL == (vtab = etch_cache_find(get_vtable_cachehkey(get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()_VTABLE)), 0)))
+ {
+ vtab = new_vtable(NULL, sizeof(vtabmask), get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()_VTABLE));
+ etch_cache_insert(((etch_object*)vtab)->get_hashkey(vtab), vtab, FALSE);
+
+ inheritlist = get_vtab_inheritance_list((etch_object*)$n.name().toString().toLowerCase(),
+ 2, 1, get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()_VTABLE)); /* create inheritance list */
+ inheritlist[1].o.obj_type = ETCHTYPEB_USER;
+ inheritlist[1].c.class_id = get_dynamic_classid_unique(&CLASSID_$n.getExtends().efqname($helper).toUpperCase());
+ }
+ ((etch_object*)$n.name().toString().toLowerCase())->vtab = vtab;
+#end
+ ((etch_object*)$n.name().toString().toLowerCase())->destroy = destroy_$n.efqname($helper);
+
+ return $n.name().toString().toLowerCase();
+}
+
+
+/**
+ * is_$n.efqname( $helper)()
+ */
+int is_$n.efqname( $helper)(void* x)
+{
+ return x && ((etch_object*)x)->class_id == CLASSID_$n.efqname( $helper).toUpperCase();
+}
+
+#end
+#end
+#end
+
+/**
+ * new_$helper.getServiceName( $intf )_service_interface
+ */
+i_$helper.getServiceName( $intf )* new_$helper.getServiceName( $intf )_service_interface ()
+{
+ i_$helper.getServiceName( $intf )* isvc = (i_$helper.getServiceName( $intf )*) new_object (sizeof(i_$intf.name().name().toLowerCase()), ETCHTYPEB_SVCINTERFACE,
+ get_dynamic_classid_unique(&CLASSID_$helper.getServiceName( $intf ).toUpperCase()_SERVICE_INTERFACE));
+
+ ((etch_object*)isvc)->destroy = destroy_$helper.getServiceName( $intf )_service_interface;
+
+#foreach($n in $intf.iterator())
+#if ($n.isMessage())
+#if (!$n.isHidden())
+ isvc->$n.name() = $helper.getServiceName( $intf )_def_$n.name();
+#end
+#end
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct())
+#if (!$n.isHidden())
+ isvc->$n.name().toString() = new_$n.efqname($helper)();
+#end
+#end
+#end
+
+ return isvc;
+}
+
+/**
+ * destroy_$helper.getServiceName( $intf )_service_interface()
+ * i_$helper.getServiceName( $intf ) destructor.
+ */
+int destroy_$helper.getServiceName($intf)_service_interface (void* data)
+{
+ i_$helper.getServiceName( $intf )* isvc = (i_$helper.getServiceName( $intf )*)data;
+ if (NULL == isvc) return -1;
+
+ if (!is_etchobj_static_content(isvc))
+ {
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct())
+#if (!$n.isHidden())
+ etch_object_destroy(isvc->$n.name().toString());
+#end
+#end
+#end
+ }
+
+ return destroy_objectex((etch_object*)isvc);
+}
+
+
+/* - - - - - - - - - - - - - -
+ * service method stubs
+ * - - - - - - - - - - - - - -
+ */
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getServiceName( $intf )_def_$n.name()(void* thisx")
+#foreach($p in $n.iterator())
+#set ($sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb)")
+$sb
+{
+#foreach( $p in $n.iterator())
+#if($p.type().isBuiltin())
+ etch_object_destroy($p.name());
+ $p.name() = NULL;
+#else
+ etch_free($p.name());
+#end
+#end
+ return NULL;
+}
+
+#end
+#end
+#end
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/intf_h.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/intf_h.vm
new file mode 100644
index 0000000..d423cce
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/intf_h.vm
@@ -0,0 +1,221 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#ifndef $helper.getIntfName( $intf ).toUpperCase()_H
+#define $helper.getIntfName( $intf ).toUpperCase()_H
+
+\#include "etch_object.h"
+\#include "etch_mailbox.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+## generate CLASSIDs
+extern unsigned short CLASSID_$helper.getIntfName( $intf ).toUpperCase()_SERVICE_INTERFACE;
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+#if (!$hasBaseClass)
+extern unsigned short CLASSID_$n.efqname($helper).toUpperCase();
+#if ($n.hasExtends())
+extern unsigned short CLASSID_$n.efqname($helper).toUpperCase()_VTABLE;
+#end
+#end
+#end
+#end
+
+## generate typedefs
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct())
+#if (!$hasBaseClass)
+//typedef struct $n.efqname($helper) $n.efqname($helper);
+#end
+#end
+#end
+
+#foreach($serviceName in $helper.getUsedServiceNames($intf))
+\#include "${serviceName.toLowerCase()}.h"
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isConstant())
+extern $helper.getNativeTypeNameForConstants( $n.type() ) $n.name();
+#end
+#end
+
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isEnumx())
+typedef enum $helper.getIntfName($intf)_$n.name()_enum
+{
+#set( $sep = "" )
+#foreach( $i in $n.iterator() )
+ $sep$n.name()_$i.name()
+#set( $sep = ", " )
+#end
+} $helper.getIntfName( $intf )_$n.name()_enum;
+
+/**
+ * $n.efqname($helper)
+ * $helper.getServiceName($intf) service value object $n.name().toString().toLowerCase()
+ */
+typedef struct $n.efqname($helper)
+{
+ etch_object object;
+
+ $helper.getIntfName( $intf )_$n.name()_enum value;
+
+ } $n.efqname($helper);
+
+$n.efqname( $helper)* new_$n.efqname($helper)();
+$n.efqname( $helper)* new_$n.efqname($helper)_init($n.efqname( $helper)_enum val);
+$n.efqname($helper)* clone_$n.efqname($helper)($n.efqname($helper)* other);
+int is_$n.efqname($helper)(void* obj);
+#end
+#end
+
+## generate stucts
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct() || $n.isExcept())
+#if (!$hasBaseClass)
+/**
+ * $n.efqname($helper)
+ * $helper.getServiceName($intf) service value object $n.name().toString().toLowerCase()
+ */
+typedef struct $n.efqname($helper)
+{
+ etch_object object;
+
+#if($n.isExcept())
+ etch_string* message;
+ uint32 errorcode;
+ excptype_t excptype;
+#end
+
+#foreach( $p in $n.getAllParameters() )
+ $helper.getNativeTypeName( $p.type(), true ) $p.name();
+#end
+
+ } $n.efqname($helper);
+
+$n.efqname( $helper)* new_$n.efqname($helper)();
+$n.efqname($helper)* clone_$n.efqname($helper)($n.efqname($helper)* other);
+## Generate setter and getter
+int is_$n.efqname($helper)(void* obj);
+
+#end
+#end
+#end
+
+
+##generate general typdefs
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#set ($sb = "typedef $helper.getPointerTypeName( $n.type() ) (*$helper.getServiceName($intf)_$n.name())(void* thisx")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName( $p.type() ) $p.name()")
+#end
+#set ($sb = "$sb);")
+$sb
+#end
+#end
+#end
+
+## generate mailbox typedef
+//typedef struct i_mailbox i_mailbox;
+
+## generate async begin typedefs
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#set ($sb = "typedef i_mailbox* (*$helper.getServiceName($intf)_async_begin_$n.name())(void* thisx")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb);")
+$sb
+#end
+#end
+#end
+
+## generate async end typedefs
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#set ($sb = "typedef $helper.getPointerTypeName( $n.type() ) (*$helper.getServiceName($intf)_async_end_$n.name())(void* thisx, i_mailbox*);")
+$sb
+#end
+#end
+#end
+
+/**
+ * i_$helper.getIntfName( $intf )
+ * $helper.getIntfName( $intf ) service interface
+ */
+typedef struct i_$helper.getIntfName( $intf )
+{
+ etch_object object;
+
+ /* - - - - - - - - - - -
+ * service virtuals
+ * - - - - - - - - - - -
+ */
+## generate service virtuals
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+ $helper.getServiceName($intf)_$n.name() $n.name();
+#end
+#end
+#end
+
+ /* - - - - - - - - - - -
+ * service data
+ * - - - - - - - - - - -
+ */
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct() || $n.isExcept())
+#if (!$n.isHidden())
+ $n.efqname($helper)* $n.name().toString();
+#end
+#end
+#end
+
+} i_$helper.getIntfName($intf);
+
+#if ($intf.hasDescr())
+/**
+#foreach( $s in $intf.descr() )
+ * $s
+#end
+ */
+#end
+i_$helper.getIntfName( $intf )* new_$helper.getIntfName($intf)_service_interface();
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* $helper.getIntfName($intf).toUpperCase()_H */
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/main_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/main_c.vm
new file mode 100644
index 0000000..b09253d
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/main_c.vm
@@ -0,0 +1,198 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+#set($i = $intf.name().name().toLowerCase())
+/*
+ * $helper.getMainFileNameI($intf, $mc)
+ */
+
+\#include "$helper.getMainFileNameH($intf, $mc)"
+\#include "etch_objecttypes.h"
+\#include "etch_runtime.h"
+\#include "etch_arrayval.h"
+\#include "etch_nativearray.h"
+\#include "etch_binary_tdo.h"
+\#include "etch_general.h"
+
+
+#if($helper.isServer($mc))
+/**
+ * new_$helper.getBaseName($intf, $suffix)
+ * create an individual client's $helper.getBaseName($intf, $suffix) implementation.
+ * this is java binding's new$intf.name().name().toLowerCase()Server().
+ * this is called back from helper.new_helper_accepted_server() (java's newServer).
+ * @param p parameter bundle. caller retains.
+ * @return the i_$helper.getBaseName($intf, $suffix), whose thisx is the $intf.name().name().toLowerCase()_server_impl.
+ */
+static void* ${i}_server_create(void* factoryData, void* sessionData)
+{
+ etch_session* session = (etch_session*)sessionData;
+ $helper.getRemoteName($intf, $helper.getRemoteDirectionName($mc))* client = ($helper.getRemoteName($intf, $helper.getRemoteDirectionName($mc))*) session->client;
+
+ $helper.getImplName($intf, $suffix)* newserver = new_$helper.getImplName($intf, $suffix)(client);
+
+ return newserver->$helper.getBaseName($intf, $suffix)_base;
+}
+
+etch_status_t ${i}_listener_start(i_sessionlistener** pplistener, wchar_t* uri, int waitupms)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = ${i}_helper_listener_create(pplistener, uri, NULL, ${i}_server_create);
+ if(etch_status == ETCH_SUCCESS)
+ {
+ etch_status = ${i}_helper_listener_start_wait(*pplistener, waitupms);
+ }
+
+ return etch_status;
+}
+
+etch_status_t ${i}_listener_stop(i_sessionlistener* plistener, int waitupms)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = ${i}_helper_listener_stop_wait(plistener, waitupms);
+ if(etch_status == ETCH_SUCCESS)
+ {
+ ${i}_helper_listener_destroy(plistener);
+ }
+
+ return etch_status;
+}
+
+#end
+#if($helper.isClient($mc))
+/**
+ * new_$helper.getBaseName($intf, $suffix)().
+ * callback constructor for client implementation object.
+ * this callback address is passed to start_$helper.getBaseName($intf, $suffix)() in [main].
+ * @param server the remote server.
+ * @remarks this callback must be supplied, i.e. its functionality cannot be
+ * defaulted, since the client implementation constructor new_$helper.getImplName($intf, $suffix)()
+ * is not known to start_$helper.getBaseName($intf, $suffix)().
+ */
+static i_$helper.getBaseName($intf, $suffix)* ${i}_client_create(void* factory_thisx, $helper.getRemoteName($intf, $helper.getRemoteDirectionName($mc))* server)
+{
+ $helper.getImplName($intf, $suffix)* client = new_$helper.getImplName($intf, $suffix)(server);
+ return client? client->$helper.getBaseName($intf, $suffix)_base:NULL;
+}
+#end
+
+#if($helper.isServer($mc))
+#ifndef NO_ETCH_SERVER_MAIN
+#end
+
+/**
+ * main()
+ */
+int main(int argc, char* argv[])
+{
+#if($helper.isServer($mc))
+ etch_status_t etch_status = ETCH_SUCCESS;
+ i_sessionlistener* listener = NULL;
+ int waitupms = 4000;
+
+ wchar_t* uri = L"tcp://0.0.0.0:4001";
+
+ etch_config_t* config = NULL;
+ etch_config_create(&config);
+
+ etch_status = etch_runtime_initialize(config);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ return 1;
+ }
+
+ etch_status = ${i}_listener_start(&listener, uri, waitupms);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+
+ // wait for keypress
+ waitkey();
+
+ etch_status = ${i}_listener_stop(listener, waitupms);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+
+ etch_status = etch_runtime_shutdown();
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ return 1;
+ }
+ etch_config_destroy(config);
+ // wait for keypress
+ waitkey();
+
+ return 0;
+#end
+#if($helper.isClient($mc))
+ etch_status_t etch_status = ETCH_SUCCESS;
+ ${i}_remote_server* remote = NULL;
+ int waitupms = 4000;
+
+ wchar_t* uri = L"tcp://127.0.0.1:4004";
+
+ etch_config_t* config = NULL;
+ etch_config_create(&config);
+ // set properties or read file
+
+ etch_status = etch_runtime_initialize(config);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ return 1;
+ }
+
+ etch_status = ${i}_helper_remote_server_create(&remote, uri, NULL, ${i}_client_create);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+
+ etch_status = ${i}_helper_remote_server_start_wait(remote, waitupms);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+
+ //add your implementation here
+
+ // wait until key press
+ waitkey();
+
+ etch_status = ${i}_helper_remote_server_stop_wait(remote, waitupms);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+
+ etch_status = ${i}_helper_remote_server_destroy(remote);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+ etch_config_destroy(config);
+ return 0;
+#end
+}
+
+#if($helper.isServer($mc))
+#endif /* NO_ETCH_SERVER_MAIN */
+#end
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/main_h.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/main_h.vm
new file mode 100644
index 0000000..259b9e3
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/main_h.vm
@@ -0,0 +1,59 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+#set($i = $intf.name().name().toLowerCase())
+/*
+ * $helper.getMainFileNameH($intf, $mc)
+ * $suffix exe main() private header
+ */
+
+#ifndef $helper.getMainFileNameH($intf, $mc).toUpperCase().replace(".","_")
+#define $helper.getMainFileNameH($intf, $mc).toUpperCase().replace(".","_")
+
+\#include "etch_runtime.h"
+\#include "$helper.getHelperFileNameH($intf)"
+\#include "$helper.getImplFileNameH($intf, $suffix)"
+\#include "$helper.getRemoteFileNameH($intf, $helper.getDirectionName($helper.getRemoteDirection($mc)))"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * ${i}_listener_start(pplistener, uri, waitupms)
+ * Start the listener at the given uri, waiting waitupms microseconds for listener startup.
+ * The created listener is saved at address pointed to by pplistener.
+ */
+extern etch_status_t ${i}_listener_start(i_sessionlistener** pplistener, wchar_t* uri, int waitupms);
+
+/*
+ * ${i}_listener_stop(plistener, waitupms)
+ * Stop the listener given by plistener, waiting waitupms microseconds to stop.
+ */
+extern etch_status_t ${i}_listener_stop(i_sessionlistener* plistener, int waitupms);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* $helper.getMainFileNameH($intf, $mc).toUpperCase() */
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/readme.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/readme.vm
new file mode 100644
index 0000000..80a309c
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/readme.vm
@@ -0,0 +1,28 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+Generated C Binding files of Apache Etch.
+
+Add your specific implementations to xxx_client_impl.c/xxx_server_impl.c
+Startup is done via xxx_listener_main.c/xxx_client_main.c
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_c.vm
new file mode 100644
index 0000000..0525e64
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_c.vm
@@ -0,0 +1,278 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+\#include "$helper.getRemoteFileNameH($intf, '')"
+\#include "etch_url.h"
+\#include "etch_objecttypes.h"
+\#include "etch_general.h"
+
+unsigned short CLASSID_$helper.getRemoteName($intf, "").toUpperCase();
+
+int destroy_$helper.getRemoteName($intf, '') (void*);
+\#if(0)
+etch_message* etchremote_new_message($helper.getRemoteName($intf, '')*, etch_type*);
+int etchremote_send($helper.getRemoteName($intf, '')*, etch_message*);
+int etchremote_begincall($helper.getRemoteName($intf, '')*, etch_message*, void**);
+int etchremote_endcall ($helper.getRemoteName($intf, '')*, i_mailbox*, etch_type*, void**);
+int etchremote_transport_control ($helper.getRemoteName($intf, '')*, etch_event*, etch_int32*);
+int etchremote_transport_notify ($helper.getRemoteName($intf, '')*, etch_event*);
+etch_object* etchremote_transport_query ($helper.getRemoteName($intf, '')*, etch_object*);
+int etchremote_start_waitup ($helper.getRemoteName($intf, '')*, const int);
+int etchremote_stop_waitdown ($helper.getRemoteName($intf, '')*, const int);
+#endif
+
+
+/* - - - - - - - - - - - - - -
+ * constructors
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_$helper.getRemoteName($intf, '')
+ * @param ids delivery service -- caller retains
+ * @param vf $intf.name().name().toLowerCase() value factory - caller retains
+ * @param i$intf.name().name().toLowerCase() optional $intf.name().name().toLowerCase() service interface -- caller retains
+ */
+$helper.getRemoteName($intf, '')* new_$helper.getRemoteName($intf, '') (void* thisx,
+ i_delivery_service* ids, etch_value_factory* vf, i_$helper.getIntfName($intf)* iservice)
+{
+ $helper.getRemoteName($intf, '')* remote = ($helper.getRemoteName($intf, '')*) new_object (sizeof($helper.getRemoteName($intf, '')),
+ ETCHTYPEB_REMOTE, get_dynamic_classid_unique(&CLASSID_$helper.getRemoteName($intf, '').toUpperCase()));
+
+ ((etch_object*)remote)->destroy = destroy_$helper.getRemoteName($intf, '');
+
+ /* $intf.name().name().toLowerCase()_remote instance data and methods */
+ remote->dsvc = ids;
+ remote->vf = vf;
+ remote->start_waitup = etchremote_start_waitup;
+ remote->stop_waitdown = etchremote_stop_waitdown;
+
+ /* transport methods */
+ remote->transport_control = etchremote_transport_control;
+ remote->transport_notify = etchremote_transport_notify;
+ remote->transport_query = etchremote_transport_query;
+
+ /* remote base */
+ remote->new_message = etchremote_new_message;
+ remote->send = etchremote_send;
+ remote->sendex = etchremote_sendex;
+ remote->begin_call = etchremote_begincall;
+ remote->end_call = etchremote_endcall;
+
+ /* $helper.getIntfName($intf) service */
+ if (iservice)
+ remote->i$helper.getIntfName($intf) = iservice;
+ else
+ { remote->i$helper.getIntfName($intf) = new_$intf.name().name().toLowerCase()_service_interface();
+ remote->is_service_interface_owned = TRUE;
+ }
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+ remote->$n.name() = remote->i$helper.getIntfName($intf)->$n.name().toString();
+#end
+#end
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct())
+#if (!$n.isHidden())
+ remote->$n.name().toString() = remote->i$helper.getIntfName($intf)->$n.name();
+#end
+#end
+#end
+
+ return remote;
+}
+
+
+/**
+ * destroy_$helper.getRemoteName($intf, '')()
+ * $helper.getRemoteName($intf, '') destructor.
+ */
+int destroy_$helper.getRemoteName($intf, '') (void* data)
+{
+ $helper.getRemoteName($intf, '')* thisx = ($helper.getRemoteName($intf, '')*)data;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ if (thisx->is_service_interface_owned && thisx->i$helper.getIntfName($intf))
+ etch_object_destroy(thisx->i$helper.getIntfName($intf));
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
+
+
+/* - - - - - - - - - - - - - -
+ * remote methods
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchremote_new_message()
+ * instantiates a message to be sent via this.send() or this.begin_call().
+ * @param thisx this remote object.
+ * @param message_type type of message, caller retains.
+ * @return message object, which could wrap an exception.
+ */
+\#if(0)
+etch_message* etchremote_new_message ($helper.getRemoteName($intf, $mc)* thisx, etch_type* message_type)
+{
+ etch_message* msg = new_message(message_type, ETCH_DEFSIZE, thisx->vf);
+ return msg;
+}
+#endif
+
+
+/**
+ * etchremote_send()
+ * sends message to recipient without waiting for a response.
+ * @param thisx this remote object.
+ * @param msg message, caller relinquishes.
+ * @return 0 success, -1 failure.
+ */
+\#if(0)
+int etchremote_send ($helper.getRemoteName($intf, $mc)* thisx, etch_message* msg)
+{
+ const int result = thisx->dsvc->itm->transport_message(thisx->dsvc, NULL, msg);
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_begincall()
+ * sends message beginning a call sequence.
+ * @param thisx this remote object.
+ * @param msg message, caller relinquishes.
+ * @return in out parameter, a mailbox which can be used to retrieve the response.
+ * @return 0 success, -1 failure.
+ */
+
+\#if(0)
+int etchremote_begincall ($helper.getRemoteName($intf, $mc)* thisx, etch_message* msg, i_mailbox** out)
+{
+ const int result = thisx->dsvc->begin_call(thisx->dsvc, msg, out);
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_endcall()
+ * finishes a call sequence by waiting for a response message.
+ * @param thisx this remote object.
+ * @param mbox a mailbox which will be used to read an expected message response.
+ * @param response_type the message type of the expected response.
+ * @return in out parameter, on success, the response.
+ * @return 0 success, -1 failure.
+ */
+\#if(0)
+int etchremote_endcall ($helper.getRemoteName($intf, $mc)* thisx, i_mailbox* mbox, etch_type* response_type, etch_object** out)
+{
+ const int result = thisx->dsvc->end_call(thisx->dsvc, mbox, response_type, out);
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_transport_control()
+ * @param evt caller relinquishes
+ * @param value caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+\#if(0)
+int etchremote_transport_control ($helper.getRemoteName($intf, $mc)* thisx, etch_event* evt, etch_int32* value)
+{
+ const int result = thisx->dsvc->itm->transport_control(thisx->dsvc, evt, value);
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_transport_notify()
+ * @param evt caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+\#if(0)
+int etchremote_transport_notify ($helper.getRemoteName($intf, $mc)* thisx, etch_event* evt)
+{
+ const int result = thisx->dsvc->itm->transport_notify(thisx->dsvc, evt);
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_transport_query()
+ * @param query caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+\#if(0)
+etch_object* etchremote_transport_query ($helper.getRemoteName($intf, $mc)* thisx, etch_object* query)
+{
+ etch_object* resultobj = thisx->dsvc->itm->transport_query(thisx->dsvc, query);
+ return resultobj;
+}
+#endif
+
+
+/**
+ * etchremote_start_waitup()
+ * start the transport and wait for it to come up.
+ * @param thisx this remote object.
+ * @param waitms how long to wait, in milliseconds.
+ * @return 0 success, -1 failure.
+ */
+\#if(0)
+int etchremote_start_waitup ($helper.getRemoteName($intf, $mc)* thisx, const int waitms)
+{
+ const int result = thisx->transport_control(thisx,
+ new_etch_event(CLASSID_CONTROL_START_WAITUP, waitms), NULL);
+
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_stop_waitdown()
+ * stop the transport and wait for it to go down.
+ * @param thisx this remote object.
+ * @param waitms how long to wait, in milliseconds.
+ * @return 0 success, -1 failure.
+ */
+\#if(0)
+int etchremote_stop_waitdown ($helper.getRemoteName($intf, $mc)* thisx, const int waitms)
+{
+ const int result = thisx->transport_control(thisx,
+ new_etch_event(CLASSID_CONTROL_STOP_WAITDOWN, waitms), NULL);
+
+ return result;
+}
+#endif
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_client_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_client_c.vm
new file mode 100644
index 0000000..6a5a7ce
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_client_c.vm
@@ -0,0 +1,480 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getRemoteName($intf, $suffix).c
+ */
+
+\#include "$helper.getRemoteFileNameH($intf, $suffix)"
+\#include "$helper.getVfFileNameH($intf)"
+\#include "etch_url.h"
+\#include "etch_log.h"
+\#include "etch_objecttypes.h"
+\#include "etch_general.h"
+
+static const char* LOG_CATEGORY = "$helper.getRemoteName($intf, $suffix)";
+
+unsigned short CLASSID_$helper.getRemoteName($intf, $suffix).toUpperCase();
+
+char* $helper.getServiceName($intf).toUpperCase()_ETCHREMC = "REMC";
+
+## generate async begin typedefs
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, '')_client_$n.name() (void*")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb);")
+$sb
+#if (!$n.isOneway())
+#set ($sb = "i_mailbox* $helper.getRemoteName($intf, '')_begin_client_$n.name() (void*")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb);")
+$sb
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, '')_end_client_$n.name()(void*, i_mailbox*);")
+$sb
+#end
+
+#end
+#end
+#end
+#end
+
+
+/* generated signatures */
+int destroy_$helper.getRemoteName($intf, $suffix) (void*);
+
+
+/* - - - - - - - -
+ * instantiation
+ * - - - - - - - -
+ */
+
+/**
+ * new_$helper.getServiceName($intf)_remote_client()
+ * $helper.getRemoteName($intf, $suffix) constructor.
+ */
+$helper.getRemoteName($intf, $suffix)* new_$helper.getServiceName($intf)_remote_client (void* thisx, etch_session* session, etch_value_factory* vf)
+{
+ $helper.getRemoteName($intf, "")* remote = NULL;
+ $helper.getRemoteName($intf, $suffix)* rc = NULL;
+
+ rc = ($helper.getRemoteName($intf, $suffix)*) new_object (sizeof($helper.getRemoteName($intf, $suffix)),
+ ETCHTYPEB_REMOTECLIENT, get_dynamic_classid_unique(&CLASSID_$helper.getRemoteName($intf, $suffix).toUpperCase()));
+
+ ((etch_object*)rc)->destroy = destroy_$helper.getRemoteName($intf, $suffix);
+
+ /* we "implement" the service interface here since it may contain client-directed
+ * items, in which case those methods and data are exposed in this object. we do
+ * not expose the service's server-directed methods in the remote client object.
+ */
+ remote = new_$helper.getRemoteName($intf, "") (thisx, session->ds, vf, NULL);
+ remote->remote_type = ETCH_REMOTETYPE_CLIENT;
+
+ rc->remote_base = remote;
+ rc->client_base = new_$helper.getBaseName($intf, $suffix)_base (thisx);
+ rc->session_id = session->session_id;
+
+ rc->vf = ($helper.getVfName($intf)*) rc->remote_base->vf;
+
+ /* override client-directed virtuals with implementations here */
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+ rc->$n.name() = $helper.getRemoteName($intf, '')_client_$n.name();
+#end
+#end
+#end
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#if (!$n.isOneway())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+ rc->async_begin_$n.name() = $helper.getRemoteName($intf, '')_begin_client_$n.name();
+ rc->async_end_$n.name() = $helper.getRemoteName($intf, '')_end_client_$n.name();
+#end
+#end
+#end
+#end
+#end
+ return rc;
+}
+
+
+/**
+ * destroy_$helper.getRemoteName($intf, $suffix)()
+ * $helper.getRemoteName($intf, $suffix) destructor.
+ */
+int destroy_$helper.getRemoteName($intf, $suffix) (void* thisx)
+{
+ $helper.getRemoteName($intf, $suffix)* remote = ($helper.getRemoteName($intf, $suffix)*)thisx;
+ if (NULL == remote) return -1;
+
+ if (!is_etchobj_static_content(remote))
+ {
+ etch_object_destroy(remote->remote_base);
+ remote->remote_base = NULL;
+ etch_object_destroy(remote->client_base);
+ remote->client_base = NULL;
+ }
+ return destroy_objectex((etch_object*)remote);
+}
+
+
+
+
+
+/**
+ * perf_remote_dispose_mailbox()
+ * dispose of mailbox after use.
+ * this is the common means of disposing of a mailbox when we're done with it.
+ * this would intuitively be part of base class code but we don't have one.
+ * @param thisx the remote server this.
+ * @param pibox pointer to pointer to the mailbox interface, passed indirectly
+ * such that this method can null out the caller's mailbox reference.
+ * @return 0 if mailbox was successfullly closed, otherwise -1.
+ * caller's i_mailbox reference is nulled out regardless of result.
+ */
+int $helper.getRemoteName($intf, "")_${suffix}_dispose_mailbox ($helper.getRemoteName($intf, $suffix)* thisx, i_mailbox** pibox)
+{
+ int result = 0;
+ i_mailbox* ibox = 0;
+ if (!pibox || !*pibox) return -1;
+ ibox = *pibox;
+ *pibox = NULL; /* null out caller's reference */
+
+ if (0 != (result = ibox->close_read (ibox)))
+ {
+ /* we should not need this failsafe unregister if close_read()
+ * is reliable, since close_read() will do the unregister */
+ i_mailbox_manager* imgr = ibox->manager(ibox);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not close mailbox %x\n", ibox);
+ if (imgr) result = imgr->unregister(imgr, ibox);
+ }
+
+ /* mailbox manager does not destroy the unregistered mailbox since it is
+ * owned by whoever registered it, that being us, so we destroy it here.
+ * debug heap issue note: this is/was the spot.
+ */
+ etch_object_destroy(ibox);
+ return result;
+}
+
+/**
+ * perf_remote_get_stubbase()
+ * convenience to return stub base object from remote server object.
+ */
+etch_stub* $helper.getRemoteName($intf, "")_${suffix}_get_stubbase ($helper.getRemoteName($intf, $suffix)* thisx)
+{
+ etch_stub* stub = NULL;
+ xxxx_either_stub* clistub = (xxxx_either_stub*) thisx->client_factory->stub;
+ stub = clistub? clistub->stub_base: NULL;
+ return stub;
+}
+
+
+/**
+ * $helper.getRemoteName($intf, "")_set_session_notify()
+ * convenience to override remote server's session_notify().
+ * @return the session_notify function that was overridden.
+ */
+etch_session_notify $helper.getRemoteName($intf, "")_${suffix}_set_session_notify($helper.getRemoteName($intf, $suffix)* thisx, etch_session_notify newfunc)
+{
+ etch_session_notify oldfunc = NULL;
+ etch_stub* stub = $helper.getRemoteName($intf, "")_${suffix}_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_notify;
+ stub->impl_callbacks->_session_notify = newfunc;
+ return oldfunc;
+}
+
+
+/**
+ * $helper.getRemoteName($intf, "")_set_session_control()
+ * convenience to override remote server's session_control().
+ * @return the session_control function that was overridden.
+ */
+etch_session_control $helper.getRemoteName($intf, "")_${suffix}_set_session_control($helper.getRemoteName($intf, $suffix)* thisx, etch_session_control newfunc)
+{
+ etch_session_control oldfunc = NULL;
+ etch_stub* stub = $helper.getRemoteName($intf, "")_${suffix}_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_control;
+ stub->impl_callbacks->_session_control = newfunc;
+ return oldfunc;
+}
+
+
+/**
+ * $helper.getRemoteName($intf, "")_set_session_query()
+ * convenience to override remote server's session_query().
+ * @return the session_query function that was overridden.
+ */
+etch_session_query $helper.getRemoteName($intf, "")_${suffix}_set_session_query($helper.getRemoteName($intf, $suffix)* thisx, etch_session_query newfunc)
+{
+ etch_session_query oldfunc = NULL;
+ etch_stub* stub = $helper.getRemoteName($intf, "")_${suffix}_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_query;
+ stub->impl_callbacks->_session_query = newfunc;
+ return oldfunc;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * remote procedure call implementations
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+
+/* - - - - - - - - - - -
+ * $helper.getIntfName($intf).$n.name()()
+ * - - - - - - - - - - -
+ */
+
+#if (!$n.isOneway())
+/**
+ * $helper.getRemoteName($intf, '')_begin_$n.name()()
+ * $helper.getIntfName($intf).$n.name() async start
+ * TODO: doc generation
+ */
+#set ($sb = "i_mailbox* $helper.getRemoteName($intf, '')_begin_client_$n.name()(void* thisx")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb)")
+$sb
+{
+ $helper.getRemoteName($intf, $suffix)* remote = ($helper.getRemoteName($intf, $suffix)*)thisx;
+ int _result = 0;
+ i_mailbox* _mbox = NULL;
+ etch_message* _msg = NULL;
+ etch_type* _msgtype = $helper.getVfName($intf)_get_static()->_mt_$helper.getServiceName($intf)_$n.name();
+
+ do
+ {
+ _msg = remote->remote_base->new_message (remote->remote_base, _msgtype);
+ if (!_msg) break;
+
+#foreach( $p in $n.iterator() )
+ _result = message_putc (_msg, $helper.getVfName($intf)_get_static()->_mf_$helper.getIntfName($intf)_$p.name(), (void**)&$p.name());
+ if ($p.name() != NULL && 0 != _result) break;
+#end
+
+ /* fyi msg memory is relinquished here regardless of result */
+ _result = remote->remote_base->begin_call(remote->remote_base, _msg, (void**)&_mbox);
+ _msg = NULL;
+
+ } while(0);
+
+ /* destroy any unrelinquished objects */
+#foreach( $p in $n.iterator() )
+ etch_object_destroy($p.name());
+ $p.name() = NULL;
+#end
+ etch_object_destroy(_msg);
+ _msg = NULL;
+ return _mbox;
+}
+
+/**
+ * $helper.getRemoteName($intf, "")_end__$n.name()()
+ * _$n.name() async end (read result from mailbox and return result)
+ * @param thisx this.
+ * @param mbox caller relinquishes
+ * @return etch_int32* result of add, caller owns.
+ */
+$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, "")_end_client_$n.name() (void* thisx, i_mailbox* ibox)
+{
+ $helper.getRemoteName($intf, $suffix)* remote = ($helper.getRemoteName($intf, $suffix)*)thisx;
+ $helper.getPointerTypeName($n.type()) _resobj = NULL;
+ int _result = -1;
+ etch_type* _restype = $helper.getVfName($intf)_get_static()->_mt_$helper.getServiceName($intf)__result_$n.name();
+
+ if(ibox == NULL) {
+ return NULL;
+ }
+
+ remote->remote_base->end_call(remote->remote_base, ibox, _restype, (void**)&_resobj);
+
+ _result = $helper.getRemoteName($intf, '')_${suffix}_dispose_mailbox (thisx, &ibox);
+ if(_result) {
+ etch_exception* excp = new_etch_exception_from_errorcode(ETCH_ERROR);
+ etch_exception_set_message(excp,new_stringw(L"can not dispose mailbox."));
+ return ($helper.getPointerTypeName($n.type()))excp;
+ }
+
+ return _resobj;
+}
+
+/**
+ * $helper.getRemoteName($intf, "")_$n.name()
+ * $helper.getIntfName($intf).$n.name() remote method call.
+ * instantiates a mailbox, sends perf.add message, waits for result to arrive
+ * in mailbox, disposes mailbox, and returns object containing add() result.
+ * @param thisx this.
+ * @param x etch_int32* caller relinquishes.
+ * @param y etch_int32* caller relinquishes.
+ * @return etch_int32* result of add, caller owns.
+ */
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, '')_client_$n.name()(void* thisx")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb)")
+$sb
+{
+ $helper.getRemoteName($intf, $suffix)* remote = ($helper.getRemoteName($intf, $suffix)*)thisx;
+ $helper.getPointerTypeName($n.type()) _resultobj = NULL;
+
+#set ($sb = "i_mailbox* _mbox = $helper.getRemoteName($intf, '')_begin_client_$n.name()(remote")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $p.name()")
+#end
+#set ($sb = "$sb);")
+ $sb
+
+ if(_mbox == NULL){
+ etch_exception* excp = new_etch_exception_from_errorcode(ETCH_EIO);
+ etch_exception_set_message(excp,new_stringw(L"can not create mailbox, connection could be down."));
+ return ($helper.getPointerTypeName($n.type()))excp;
+ }
+
+ _resultobj = $helper.getRemoteName($intf, '')_end_client_$n.name()(remote, _mbox);
+
+ return _resultobj;
+}
+
+## generate oneway
+#else
+/**
+ * $helper.getRemoteName($intf, '')_begin_$n.name()()
+ * $helper.getIntfName($intf).$n.name() async start
+ * @param thisx this.
+ * @param x etch_int32* caller relinquishes.
+ * @param y etch_int32* caller relinquishes.
+ * @return mailbox to receive async result. caller owns it. it may be null.
+ * @remarks note that we use the putc version of message.put(), wherein we pass
+ * a *reference* to the value object's pointer, and message.putc() nulls out
+ * the reference. this permits us to break when a putc() error occurs, without
+ * leaking the un-put parameters, since prior to exit we can destroy each
+ * parameter which remains non-null.
+ * @remarks note also that we don't bother to clone the value keys, since they
+ * are protected objects, and while message_put will appear to destroy them,
+ * this will have no effect.
+ */
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, '')_begin_client_$n.name()(void* thisx")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb)")
+$sb
+{
+ $helper.getRemoteName($intf, $suffix)* remote = ($helper.getRemoteName($intf, $suffix)*)thisx;
+ int _result = 0;
+ etch_message* _msg = NULL;
+ $helper.getPointerTypeName($n.type()) _resultobj = NULL;
+ etch_type* _msgtype = $helper.getVfName($intf)_get_static()->_mt_$helper.getServiceName($intf)_$n.name();
+
+ do
+ {
+ _msg = remote->remote_base->new_message (remote->remote_base, _msgtype);
+ if (!_msg) break;
+
+#foreach( $p in $n.iterator() )
+ _result = message_putc (_msg, $helper.getVfName($intf)_get_static()->_mf_$helper.getIntfName($intf)_$p.name(), (void**)&$p.name());
+ if ($p.name() != NULL && 0 != _result) break;
+#end
+
+ /* fyi msg memory is relinquished here regardless of result */
+ _resultobj = remote->remote_base->sendex (remote->remote_base, _msg);
+ _msg = NULL;
+
+ } while(0);
+
+ /* destroy any unrelinquished objects */
+#foreach( $p in $n.iterator() )
+ etch_object_destroy($p.name());
+ $p.name() = NULL;
+#end
+ etch_object_destroy(_msg);
+ _msg = NULL;
+ return _resultobj;
+}
+
+/**
+ * $helper.getRemoteName($intf, '')_end_$n.name()()
+ * async result handler not used since $helper.getIntfName($intf).$n.name() is a one-way send-and-forget method.
+ */
+void* $helper.getRemoteName($intf, '')_end_client_$n.name() ($helper.getRemoteName($intf, $suffix)* thisx, i_mailbox* mbox)
+{
+ return NULL;
+}
+
+/**
+ * $helper.getRemoteName($intf, '')_$n.name()()
+ * $helper.getIntfName($intf).$n.name() remote 1-way method call.
+ * sends $helper.getIntfName($intf).$n.name() message and does not wait for a result. if server implementation
+ * code throws an exception, the exception arrives via session_notify override if any.
+ * @param code an integer object, caller relinquishes.
+ * @param x etch_string caller relinquishes.
+ * @return integer transport result code cast to void*.
+ */
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, '')_client_$n.name()(void* thisx")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb)")
+$sb
+{
+ $helper.getRemoteName($intf, $suffix)* remote = ($helper.getRemoteName($intf, $suffix)*)thisx;
+ /* $helper.getIntfName($intf).$n.name() is a one-way, send-and-forget message so we only do a begin call */
+#set ($sb = "$helper.getPointerTypeName($n.type()) _resultobj = $helper.getRemoteName($intf, '')_begin_client_$n.name()(remote")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $p.name()")
+#end
+#set ($sb = "$sb);")
+ $sb
+
+ return _resultobj;
+}
+
+#end
+#end
+#end
+#end
+#end
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_client_h.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_client_h.vm
new file mode 100644
index 0000000..c54652f
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_client_h.vm
@@ -0,0 +1,109 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getRemoteFileNameH($intf, $suffix)
+ */
+
+#ifndef $helper.getRemoteName($intf, $suffix).toUpperCase()_H
+#define $helper.getRemoteName($intf, $suffix).toUpperCase()_H
+
+\#include "$helper.getRemoteFileNameH($intf, "")"
+\#include "$helper.getBaseFileNameH($intf, $suffix)"
+\#include "etch_transport.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_$helper.getRemoteName($intf, $suffix).toUpperCase();
+
+/**
+ * $helper.getRemoteName($intf, $suffix)
+ */
+typedef struct $helper.getRemoteName($intf, $suffix)
+{
+ etch_object object;
+
+ i_$helper.getBaseName($intf, $suffix)* client_base; /* owned */
+ $helper.getRemoteName($intf, "")* remote_base; /* owned */
+ etch_client_factory* client_factory; /* owned */
+ default_value_factory* vf; /* owned by base */
+ int session_id;
+
+ /* toward-client virtuals go here */
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden() && ($n.isMsgDirBoth() || $n.isMsgDirClient()))
+ $helper.getServiceName( $intf )_$n.name() $n.name();
+#end
+#end
+#end
+/*
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct())
+#if (!$n.isHidden())
+ $n.efqname($helper)* $n.name().toString().toLowerCase();
+#end
+#end
+#end
+*/
+
+
+ /* private, generally. since unit tests invoke async begin and end,
+ * we must expose them either as virtuals or as external references.
+ */
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden()&& ($n.isMsgDirBoth() || $n.isMsgDirClient()))
+#if (!$n.isOneway())
+ $helper.getServiceName( $intf )_async_begin_$n.name() async_begin_$n.name();
+#end
+#end
+#end
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden()&& ($n.isMsgDirBoth() || $n.isMsgDirClient()))
+#if (!$n.isOneway())
+ $helper.getServiceName( $intf )_async_end_$n.name() async_end_$n.name();
+#end
+#end
+#end
+#end
+
+
+} $helper.getRemoteName($intf, $suffix);
+
+/* constructor */
+$helper.getRemoteName($intf, $suffix)* new_$helper.getServiceName($intf)_remote_client (void*, etch_session*, etch_value_factory*);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* $helper.getRemoteName($intf, $suffix).toUpperCase()_H */
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_h.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_h.vm
new file mode 100644
index 0000000..df407e0
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_h.vm
@@ -0,0 +1,110 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getRemoteFileNameH($intf, $mc)
+ * $helper.getIntfName($intf) remote.
+ * combines java bindings's RemotePerf, Perf, and RemoteBase.
+ */
+
+#ifndef $helper.getRemoteName($intf, "").toUpperCase()_H
+#define $helper.getRemoteName($intf, "").toUpperCase()_H
+
+\#include "$helper.getIntfFileNameH($intf)"
+\#include "etch_remote.h"
+\#include "etch_transport.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_$helper.getRemoteName($intf, "").toUpperCase();
+
+/**
+ * $helper.getRemoteName($intf, $suffix)
+ */
+typedef struct $helper.getRemoteName($intf, "")
+{
+ etch_object object;
+
+ i_$helper.getIntfName($intf)* i$helper.getIntfName($intf); /* possibly owned */
+ i_delivery_service* dsvc; /* not owned */
+ etch_value_factory* vf; /* not owned */
+ unsigned char remote_type; /* client or server */
+ unsigned char is_service_interface_owned;
+ unsigned short unused; /* alignment */
+
+ etch_message* (*new_message) (void*, etch_type*);
+ int (*send) (void*, etch_message*);
+ void* (*sendex) (void*, etch_message*);
+ etch_delivsvc_begincall begin_call; /* i_mailbox** out */
+ etch_delvisvc_endcall end_call; /* etch_object** out */
+
+ int (*start_waitup) (void*, const int waitms);
+ int (*stop_waitdown) (void*, const int waitms);
+
+ /* - - - - - - - - - - - - -
+ * transport functionality
+ * - - - - - - - - - - - - -
+ */
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ /* - - - - - - - - - - -
+ * service virtuals
+ * - - - - - - - - - - -
+ */
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+ $helper.getIntfName( $intf )_$n.name() $n.name();
+#end
+#end
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct())
+#if (!$n.isHidden())
+ $n.efqname($helper)* $n.name().toString();
+#end
+#end
+#end
+
+ /* - - - - - - - - - - -
+ * private instance data
+ * - - - - - - - - - - -
+ */
+
+} $helper.getRemoteName($intf, "");
+
+
+$helper.getRemoteName($intf, "")* new_$helper.getRemoteName($intf, "") (void*, i_delivery_service*, etch_value_factory*, i_$helper.getIntfName($intf)*);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+#endif /* $helper.getRemoteName($intf, "").toUpperCase()_H */
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_server_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_server_c.vm
new file mode 100644
index 0000000..8c091bb
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_server_c.vm
@@ -0,0 +1,475 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getRemoteFileNameI($intf, $suffix)
+ * generated remote procedure calls.
+ */
+
+\#include "$helper.getRemoteFileNameH($intf, $suffix)"
+\#include "etch_plain_mailbox_manager.h"
+\#include "etch_svcobj_masks.h"
+\#include "etch_objecttypes.h"
+\#include "etch_exception.h"
+\#include "etch_url.h"
+\#include "etch_log.h"
+\#include "etch_general.h"
+
+unsigned short CLASSID_$helper.getRemoteName($intf, $suffix).toUpperCase();
+
+char* $helper.getServiceName($intf).toUpperCase()_ETCHREMS = "REMS";
+
+## generate async begin typedefs
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, '')_server_$n.name() (void* thisAsVoid")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb);")
+$sb
+#if (!$n.isOneway())
+#set ($sb = "i_mailbox* $helper.getRemoteName($intf, '')_begin_server_$n.name() (void* thisAsVoid")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb);")
+$sb
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, '')_end_server_$n.name()(void*, i_mailbox*);")
+$sb
+#end
+
+#end
+#end
+#end
+#end
+
+int destroy_$helper.getRemoteName($intf, $suffix) (void*);
+
+/* - - - - - - - - - -
+ * instantiation etc.
+ * - - - - - - - - - -
+ */
+
+/**
+ * new_$helper.getRemoteName($intf, $suffix)()
+ * $helper.getRemoteName($intf, $suffix) constructor.
+ * @param thisx owner, optional, null in practice.
+ * @param ids delivery service interface, caller retains.
+ * @param vf service specific value factory, caller retains.
+ */
+$helper.getRemoteName($intf, $suffix)* new_$helper.getRemoteName($intf, $suffix) (void* thisx, i_delivery_service* ids, etch_value_factory* vf)
+{
+ $helper.getRemoteName($intf, $suffix)* rs = ($helper.getRemoteName($intf, $suffix)*) new_object (sizeof($helper.getRemoteName($intf, $suffix)),
+ ETCHTYPEB_REMOTESERVER, get_dynamic_classid_unique(&CLASSID_$helper.getRemoteName($intf, $suffix).toUpperCase()));
+
+ ((etch_object*)rs)->destroy = destroy_$helper.getRemoteName($intf, $suffix);
+
+ rs->remote_base = new_$helper.getRemoteName($intf, "") (thisx, ids, vf, NULL);
+ rs->remote_base->remote_type = ETCH_REMOTETYPE_SERVER;
+
+ /* 2/3/09 now passing perf interface not owned by server_base */
+ rs->server_base = new_$helper.getRemoteName($intf, $suffix)_base (thisx, rs->remote_base->i$helper.getIntfName($intf));
+
+ rs->vf = ($helper.getVfName($intf)*) rs->remote_base->vf;
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+ rs->$n.name() = $helper.getRemoteName($intf, '')_server_$n.name();
+#end
+#end
+#end
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#if (!$n.isOneway())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+ rs->async_begin_$n.name() = $helper.getRemoteName($intf, '')_begin_server_$n.name();
+ rs->async_end_$n.name() = $helper.getRemoteName($intf, '')_end_server_$n.name();
+#end
+#end
+#end
+#end
+#end
+
+ return rs;
+}
+
+/**
+ * destroy_$helper.getBaseName($intf, $suffix)_base()
+ * i_$helper.getBaseName($intf, $suffix) destructor.
+ */
+int destroy_$helper.getRemoteName($intf, $suffix) (void* thisAsVoid)
+{
+ $helper.getRemoteName($intf, $suffix)* thisx = ($helper.getRemoteName($intf, $suffix)*)thisAsVoid;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ etch_object_destroy(thisx->remote_base);
+ thisx->remote_base = NULL;
+ etch_object_destroy(thisx->server_base);
+ thisx->server_base = NULL;
+ etch_object_destroy(thisx->client_factory);
+ thisx->client_factory = NULL;
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
+
+
+/**
+ * perf_remote_dispose_mailbox()
+ * dispose of mailbox after use.
+ * this is the common means of disposing of a mailbox when we're done with it.
+ * this would intuitively be part of base class code but we don't have one.
+ * @param thisx the remote server this.
+ * @param pibox pointer to pointer to the mailbox interface, passed indirectly
+ * such that this method can null out the caller's mailbox reference.
+ * @return 0 if mailbox was successfullly closed, otherwise -1.
+ * caller's i_mailbox reference is nulled out regardless of result.
+ */
+int $helper.getRemoteName($intf, "")_${suffix}_dispose_mailbox ($helper.getRemoteName($intf, $suffix)* thisx, i_mailbox** pibox)
+{
+ int result = 0;
+ i_mailbox* ibox = 0;
+ if (!pibox || !*pibox) return -1;
+ ibox = *pibox;
+ *pibox = NULL; /* null out caller's reference */
+
+ if (0 != (result = ibox->close_read (ibox)))
+ {
+ /* we should not need this failsafe unregister if close_read()
+ * is reliable, since close_read() will do the unregister */
+ i_mailbox_manager* imgr = ibox->manager(ibox);
+ ETCH_LOG($helper.getServiceName($intf).toUpperCase()_ETCHREMS, ETCH_LOG_ERROR, "could not close mailbox %x\n", ibox);
+ if (imgr) result = imgr->unregister(imgr, ibox);
+ }
+
+ /* mailbox manager does not destroy the unregistered mailbox since it is
+ * owned by whoever registered it, that being us, so we destroy it here.
+ * debug heap issue note: this is/was the spot.
+ */
+ etch_object_destroy(ibox);
+ return result;
+}
+
+etch_stub* $helper.getRemoteName($intf, "")_${suffix}_get_stubbase ($helper.getRemoteName($intf, $suffix)* thisx)
+{
+ etch_stub* stub = NULL;
+ xxxx_either_stub* clistub = (xxxx_either_stub*) thisx->client_factory->stub;
+ stub = clistub? clistub->stub_base: NULL;
+ return stub;
+}
+
+
+/**
+ * $helper.getRemoteName($intf, "")_set_session_notify()
+ * convenience to override remote server's session_notify().
+ * @return the session_notify function that was overridden.
+ */
+etch_session_notify $helper.getRemoteName($intf, "")_${suffix}_set_session_notify($helper.getRemoteName($intf, $suffix)* thisx, etch_session_notify newfunc)
+{
+ etch_session_notify oldfunc = NULL;
+ etch_stub* stub = $helper.getRemoteName($intf, "")_${suffix}_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_notify;
+ stub->impl_callbacks->_session_notify = newfunc;
+ return oldfunc;
+}
+
+
+/**
+ * $helper.getRemoteName($intf, "")_set_session_control()
+ * convenience to override remote server's session_control().
+ * @return the session_control function that was overridden.
+ */
+etch_session_control $helper.getRemoteName($intf, "")_${suffix}_set_session_control($helper.getRemoteName($intf, $suffix)* thisx, etch_session_control newfunc)
+{
+ etch_session_control oldfunc = NULL;
+ etch_stub* stub = $helper.getRemoteName($intf, "")_${suffix}_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_control;
+ stub->impl_callbacks->_session_control = newfunc;
+ return oldfunc;
+}
+
+
+/**
+ * $helper.getRemoteName($intf, "")_set_session_query()
+ * convenience to override remote server's session_query().
+ * @return the session_query function that was overridden.
+ */
+etch_session_query $helper.getRemoteName($intf, "")_${suffix}_set_session_query($helper.getRemoteName($intf, $suffix)* thisx, etch_session_query newfunc)
+{
+ etch_session_query oldfunc = NULL;
+ etch_stub* stub = $helper.getRemoteName($intf, "")_${suffix}_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_query;
+ stub->impl_callbacks->_session_query = newfunc;
+ return oldfunc;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * remote procedure call implementations
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+
+/* - - - - - - - - - - -
+ * $helper.getIntfName($intf).$n.name()()
+ * - - - - - - - - - - -
+ */
+
+#if (!$n.isOneway())
+/**
+ * $helper.getRemoteName($intf, '')_begin_$n.name()()
+ * $helper.getIntfName($intf).$n.name() async start
+ * TODO: doc generation
+ */
+#set ($sb = "i_mailbox* $helper.getRemoteName($intf, '')_begin_server_$n.name()(void* thisAsVoid")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb)")
+$sb
+{
+ $helper.getRemoteName($intf, $suffix)* thisx = ($helper.getRemoteName($intf, $suffix)*)thisAsVoid;
+ int _result = 0;
+ i_mailbox* _mbox = NULL;
+ etch_message* _msg = NULL;
+ etch_type* _msgtype = $helper.getVfName($intf)_get_static()->_mt_$helper.getServiceName($intf)_$n.name();
+
+ do
+ {
+ _msg = thisx->remote_base->new_message (thisx->remote_base, _msgtype);
+ if (!_msg) break;
+
+#foreach( $p in $n.iterator() )
+ _result = message_putc (_msg, $helper.getVfName($intf)_get_static()->_mf_$helper.getIntfName($intf)_$p.name(), (void**)&$p.name());
+ if ($p.name() != NULL && 0 != _result) break;
+#end
+
+ /* fyi msg memory is relinquished here regardless of result */
+ _result = thisx->remote_base->begin_call(thisx->remote_base, _msg, (void**)&_mbox);
+ _msg = NULL;
+
+ } while(0);
+
+ /* destroy any unrelinquished objects */
+#foreach( $p in $n.iterator() )
+ etch_object_destroy($p.name());
+ $p.name() = NULL;
+#end
+ etch_object_destroy(_msg);
+ _msg = NULL;
+
+ return _mbox;
+}
+
+/**
+ * $helper.getRemoteName($intf, "")_end__$n.name()()
+ * _$n.name() async end (read result from mailbox and return result)
+ * @param thisx this.
+ * @param mbox caller relinquishes
+ * @return etch_int32* result of add, caller owns.
+ */
+$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, "")_end_server_$n.name() (void* thisAsVoid, i_mailbox* ibox)
+{
+ $helper.getRemoteName($intf, $suffix)* thisx = ($helper.getRemoteName($intf, $suffix)*)thisAsVoid;
+ $helper.getPointerTypeName($n.type()) _resobj = NULL;
+ int _result = -1;
+ etch_type* _restype = $helper.getVfName($intf)_get_static()->_mt_$helper.getServiceName($intf)__result_$n.name();
+
+ if(ibox == NULL) {
+ return NULL;
+ }
+
+ thisx->remote_base->end_call(thisx->remote_base, ibox, _restype, (void**)&_resobj);
+
+ _result = $helper.getRemoteName($intf, '')_${suffix}_dispose_mailbox (thisx, &ibox);
+ if(_result) {
+ etch_exception* excp = new_etch_exception_from_errorcode(ETCH_ERROR);
+ etch_exception_set_message(excp,new_stringw(L"can not dispose mailbox."));
+ return ($helper.getPointerTypeName($n.type()))excp;
+ }
+
+ return _resobj;
+}
+
+/**
+ * $helper.getRemoteName($intf, "")_$n.name()
+ * $helper.getIntfName($intf).$n.name() remote method call.
+ * instantiates a mailbox, sends perf.add message, waits for result to arrive
+ * in mailbox, disposes mailbox, and returns object containing add() result.
+ * @param thisx this.
+ * @param x etch_int32* caller relinquishes.
+ * @param y etch_int32* caller relinquishes.
+ * @return etch_int32* result of add, caller owns.
+ */
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, '')_server_$n.name()(void* thisAsVoid")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb)")
+$sb
+{
+ $helper.getRemoteName($intf, $suffix)* thisx = ($helper.getRemoteName($intf, $suffix)*)thisAsVoid;
+ $helper.getPointerTypeName($n.type()) _resultobj = NULL;
+
+#set ($sb = "i_mailbox* _mbox = $helper.getRemoteName($intf, '')_begin_server_$n.name()(thisx")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $p.name()")
+#end
+#set ($sb = "$sb);")
+ $sb
+
+ if(_mbox == NULL){
+ etch_exception* excp = new_etch_exception_from_errorcode(ETCH_EIO);
+ etch_exception_set_message(excp,new_stringw(L"can not create mailbox, connection could be down."));
+ return ($helper.getPointerTypeName($n.type()))excp;
+ }
+
+ _resultobj = $helper.getRemoteName($intf, "")_end_server_$n.name() (thisx, _mbox);
+
+ return _resultobj;
+}
+
+## generate oneway
+#else
+/**
+ * $helper.getRemoteName($intf, '')_begin_$n.name()()
+ * $helper.getIntfName($intf).$n.name() async start
+ * @param thisx this.
+ * @param x etch_int32* caller relinquishes.
+ * @param y etch_int32* caller relinquishes.
+ * @return mailbox to receive async result. caller owns it. it may be null.
+ * @remarks note that we use the putc version of message.put(), wherein we pass
+ * a *reference* to the value object's pointer, and message.putc() nulls out
+ * the reference. this permits us to break when a putc() error occurs, without
+ * leaking the un-put parameters, since prior to exit we can destroy each
+ * parameter which remains non-null.
+ * @remarks note also that we don't bother to clone the value keys, since they
+ * are protected objects, and while message_put will appear to destroy them,
+ * this will have no effect.
+ */
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, '')_begin_server_$n.name()(void* thisAsVoid")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb)")
+$sb
+{
+ $helper.getRemoteName($intf, $suffix)* thisx = ($helper.getRemoteName($intf, $suffix)*)thisAsVoid;
+#foreach( $p in $n.iterator() )
+#set($result_needed = true)
+#end
+#if($result_needed)
+ int _result = 0;
+#end
+ etch_message* _msg = NULL;
+ $helper.getPointerTypeName($n.type()) _resultobj = NULL;
+ etch_type* _msgtype = $helper.getVfName($intf)_get_static()->_mt_$helper.getServiceName($intf)_$n.name();
+
+ do
+ {
+ _msg = thisx->remote_base->new_message (thisx->remote_base, _msgtype);
+ if (!_msg) break;
+
+#foreach( $p in $n.iterator() )
+ _result = message_putc (_msg, $helper.getVfName($intf)_get_static()->_mf_$helper.getIntfName($intf)_$p.name(), (void**)&$p.name());
+ if ($p.name() != NULL && 0 != _result) break;
+#end
+
+ /* fyi msg memory is relinquished here regardless of result */
+ /* fyi msg memory is relinquished here regardless of result */
+ _resultobj = thisx->remote_base->sendex (thisx->remote_base, _msg);
+ _msg = NULL;
+
+ } while(0);
+
+ /* destroy any unrelinquished objects */
+#foreach( $p in $n.iterator() )
+ etch_object_destroy($p.name());
+ $p.name() = NULL;
+#end
+ etch_object_destroy(_msg);
+ _msg = NULL;
+ return _resultobj;
+}
+
+/**
+ * $helper.getRemoteName($intf, '')_end_$n.name()()
+ * async result handler not used since $helper.getIntfName($intf).$n.name() is a one-way send-and-forget method.
+ */
+void* $helper.getRemoteName($intf, '')_end_server_$n.name() (void* thisAsVoid, i_mailbox* mbox)
+{
+ return NULL;
+}
+
+/**
+ * $helper.getRemoteName($intf, '')_$n.name()()
+ * $helper.getIntfName($intf).$n.name() remote 1-way method call.
+ * sends $helper.getIntfName($intf).$n.name() message and does not wait for a result. if server implementation
+ * code throws an exception, the exception arrives via session_notify override if any.
+ * @param code an integer object, caller relinquishes.
+ * @param x etch_string caller relinquishes.
+ * @return integer transport result code cast to void*.
+ */
+#set ($sb = "$helper.getPointerTypeName($n.type()) $helper.getRemoteName($intf, '')_server_$n.name()(void* thisAsVoid")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $helper.getTypeName($p.type()) $p.name()")
+#end
+#set ($sb = "$sb)")
+$sb
+{
+ $helper.getRemoteName($intf, $suffix)* thisx = ($helper.getRemoteName($intf, $suffix)*)thisAsVoid;
+ /* $helper.getIntfName($intf).$n.name() is a one-way, send-and-forget message so we only do a begin call */
+#set ($sb = "$helper.getPointerTypeName($n.type()) _resultobj = $helper.getRemoteName($intf, '')_begin_server_$n.name()(thisx")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, $p.name()")
+#end
+#set ($sb = "$sb);")
+ $sb
+
+ return _resultobj;
+}
+
+#end
+#end
+#end
+#end
+#end
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_server_h.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_server_h.vm
new file mode 100644
index 0000000..b067ae4
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/remote_server_h.vm
@@ -0,0 +1,117 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getRemoteFileNameH($intf, $suffix)
+ * generated remote procedure calls.
+ */
+
+#ifndef $helper.getRemoteName($intf, $suffix).toUpperCase()_H
+#define $helper.getRemoteName($intf, $suffix).toUpperCase()_H
+
+\#include "$helper.getRemoteFileNameH($intf, "")"
+\#include "$helper.getBaseFileNameH($intf, $suffix)"
+\#include "$helper.getVfFileNameH($intf)"
+\#include "etch_transport.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_$helper.getRemoteName($intf, $suffix).toUpperCase();
+
+
+/**
+ * $helper.getRemoteName($intf, $suffix)
+ */
+typedef struct $helper.getRemoteName($intf, $suffix)
+{
+ etch_object object;
+
+ i_$helper.getBaseName($intf, $suffix)* server_base; /* owned */
+ $helper.getRemoteName($intf, "")* remote_base; /* owned */
+ etch_client_factory* client_factory; /* owned */
+ $helper.getVfName($intf)* vf; /* owned by base */
+ int server_id;
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden()&& ($n.isMsgDirBoth() || $n.isMsgDirServer()))
+ $helper.getServiceName( $intf )_$n.name() $n.name();
+#end
+#end
+#end
+
+/*
+#foreach( $n in $intf.iterator() )
+#if ($n.isStruct())
+#if (!$n.isHidden())
+ $n.efqname($helper)* $n.name().toString().toLowerCase();
+#end
+#end
+#end
+*/
+
+ /* private, generally. since unit tests invoke async begin and end,
+ * we must expose them either as virtuals or as external references.
+ */
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden()&& ($n.isMsgDirBoth() || $n.isMsgDirServer()))
+#if (!$n.isOneway())
+ $helper.getServiceName( $intf )_async_begin_$n.name() async_begin_$n.name();
+#end
+#end
+#end
+#end
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden()&& ($n.isMsgDirBoth() || $n.isMsgDirServer()))
+#if (!$n.isOneway())
+ $helper.getServiceName( $intf )_async_end_$n.name() async_end_$n.name();
+#end
+#end
+#end
+#end
+
+} $helper.getRemoteName($intf, $suffix);
+
+
+/* constructor */
+$helper.getRemoteName($intf, $suffix)* new_$helper.getRemoteName($intf, $suffix)(void*, i_delivery_service*, etch_value_factory*);
+
+int $helper.getRemoteName($intf, "")_dispose_mailbox($helper.getRemoteName($intf, $suffix)*, i_mailbox**);
+
+/* convenience methods to override client's session interface methods */
+etch_stub* $helper.getRemoteName($intf, "")_get_stubbase ($helper.getRemoteName($intf, $suffix)*);
+etch_session_notify $helper.getRemoteName($intf, "")_set_session_notify($helper.getRemoteName($intf, $suffix)*, etch_session_notify);
+etch_session_control $helper.getRemoteName($intf, "")_set_session_control($helper.getRemoteName($intf, $suffix)*, etch_session_control);
+etch_session_query $helper.getRemoteName($intf, "")_set_session_query($helper.getRemoteName($intf, $suffix)*, etch_session_query);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* $helper.getRemoteName($intf, $suffix).toUpperCase()_H */
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/stub_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/stub_c.vm
new file mode 100644
index 0000000..59ff496
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/stub_c.vm
@@ -0,0 +1,291 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getStubFileNameI($intf, $suffix)
+ */
+
+\#include "$helper.getBaseFileNameH($intf, $suffix)"
+\#include "$helper.getStubFileNameH($intf, $suffix)"
+\#include "$helper.getVfFileNameH($intf)"
+
+\#include "etch_url.h"
+\#include "etch_objecttypes.h"
+\#include "etch_svcobj_masks.h"
+\#include "etch_general.h"
+
+#if($helper.isServer($mc))
+\#include "etch_exception.h"
+\#include "etch_log.h"
+#end
+
+unsigned short CLASSID_$helper.getStubName($intf, $suffix).toUpperCase();
+
+#if($helper.isServer($mc))
+char* $helper.getServiceName($intf).toUpperCase()_$helper.getServiceName($intf).toUpperCase()_ETCHSTBI = "STBI";
+#end
+
+int destroy_$helper.getStubName($intf, $suffix)(void*);
+
+
+/* - - - - - - - - - - -
+ * stub helper methods
+ * - - - - - - - - - - -
+ */
+
+## check if any Message goes to $suffix direction
+#if(!$intf.hasMessageDirection($mc) && $helper.hasMessageDirectionBoth($intf))
+/**
+ * $helper.getStubName($intf, $suffix)_run_nothing_
+ */
+int $helper.getStubName($intf, $suffix)_run_nothing_ (etch_stub* stub, i_delivery_service* dsvc,
+ void* obj, etch_who* whofrom, etch_message* msg)
+{
+ i_$helper.getBaseName($intf, $suffix)* client = (i_$helper.getBaseName($intf, $suffix)*)obj;
+ $helper.getVfName($intf)_impl* pvfi = NULL;
+ $helper.getVfName($intf)* pvf = NULL;
+ struct $helper.getBaseName($intf, $suffix)_impl* impl = NULL;
+
+ /* objects specific to service.nothing_() */
+ etch_field* key_foo = NULL;
+ etch_int64* val_foo = NULL;
+ etch_field* key_bar = NULL;
+ etch_string* val_bar = NULL;
+ etch_int32* resultobj = NULL;
+
+ etchstub_validate_args (stub, dsvc, msg, client, &pvf, (void**)&pvfi, (void**)&impl);
+
+ key_foo = NULL;
+ key_bar = NULL;
+ ETCH_ASSERT(key_foo && key_bar);
+
+ /* nullarg asserts are initial tests only: server impl
+ * will generate exceptions on nullargs in the real world */
+ val_foo = NULL; /* = (etch_int64*) message_get (msg, key_foo); */
+ val_bar = NULL; /* = (etch_string*) message_get (msg, key_bar); */
+ ETCH_ASSERT(val_foo && val_bar);
+
+ /* execute the service method */
+ resultobj = NULL; /* server->nothing_ (impl, val_foo, val_bar); */
+ ETCH_ASSERT(resultobj);
+
+ /* transmit reply back to sender */
+ return etchstub_send_reply (stub, dsvc, whofrom, msg, (void*) resultobj, TRUE);
+}
+#end
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+#if (!$n.isHidden())
+/**
+ * $helper.getStubName($intf, '')_run_$n.name()
+ */
+int $helper.getStubName($intf, '')_run_$helper.getDirectionName($mc)_$n.name()(etch_stub* stub, i_delivery_service* dsvc, void* obj, etch_who* whofrom, etch_message* msg)
+{
+ i_$helper.getBaseName($intf, $suffix)* $suffix = (i_$helper.getBaseName($intf, $suffix)*)obj;
+ int returnCode = 0;
+ $helper.getVfName($intf)_impl* pvfi = NULL;
+ $helper.getVfName($intf)* pvf = NULL;
+ struct $helper.getBaseName($intf, $suffix)_impl* impl = NULL;
+
+ /* objects specific to $helper.getBaseName($intf, $suffix).$name.name()() */
+#foreach( $p in $n.iterator() )
+ etch_field* key_$p.name() = NULL;
+ $helper.getPointerTypeName($p.type()) val_$p.name() = NULL;
+#end
+#if($n.hasReturn() && !$n.isOneWay())
+ $helper.getPointerTypeName($n.getResultMessage().getResultParam().type()) resultobj = NULL;
+#end
+#if(!$n.hasReturn() && !$n.isOneWay())
+ void* resultobj = NULL;
+#end
+ etchstub_validate_args (stub, dsvc, msg, $suffix, &pvf, (void**)&pvfi, (void**)&impl);
+
+#set($op = '')
+#set($sb = '')
+#foreach($p in $n.iterator())
+#set($sb = "$sb${op}key_$p.name()")
+ key_$p.name() = $helper.getVfName($intf)_get_static()->_mf_$helper.getIntfName($intf)_$p.name();
+#set($op = ' && ')
+#end
+#if($sb != '')
+ ETCH_ASSERT($sb);
+#end
+
+#set($op = '')
+#set($sb = '')
+#foreach($p in $n.iterator())
+##set($sb = "$sb${op}val_$p.name()")
+ val_$p.name() = ($helper.getPointerTypeName($p.type())) message_remove(msg, key_$p.name());
+##set($op = ' && ')
+#end
+##if($sb != '')
+## ETCH_ASSERT($sb);
+##end
+
+ /* execute the service method */
+#set($sb = '')
+#if($n.hasReturn() || $n.hasThrown())
+ #set ( $sb = "resultobj = ")
+#end
+#set ($sb = "$sb ${suffix}->$n.name() (impl")
+#foreach( $p in $n.iterator() )
+#set ( $sb = "$sb, val_$p.name()")
+#end
+#set ($sb = "$sb);")
+ $sb
+
+#if(!$n.isOneway() && !$n.hasReturn() && !$n.hasThrown())
+ /* transmit reply back to sender */
+ returnCode = etchstub_send_reply (stub, dsvc, whofrom, msg, (void*) resultobj, FALSE);
+#elseif(!$n.isOneway() && !$n.hasReturn() && $n.hasThrown())
+ returnCode = etchstub_send_reply (stub, dsvc, whofrom, msg, (void*) resultobj, resultobj != NULL);
+#elseif(!$n.isOneway() && $n.hasReturn() && !$helper.isRefType($n.getResultMessage().getResultParam().type()))
+ returnCode = etchstub_send_reply (stub, dsvc, whofrom, msg, (void*) resultobj, TRUE);
+#elseif(!$n.isOneway() && $n.hasReturn() && $helper.isRefType($n.getResultMessage().getResultParam().type()))
+ returnCode = etchstub_send_reply (stub, dsvc, whofrom, msg, (void*) resultobj, resultobj != NULL);
+#elseif($n.isOneway())
+ if(resultobj != NULL) {
+ returnCode = etchstub_send_reply (stub, dsvc, whofrom, msg, (void*) resultobj, TRUE);
+ }
+#end
+ if(!returnCode){
+ etch_object_destroy(msg);
+ }
+ return returnCode;
+}
+#end
+#end
+#end
+#end
+
+/* - - - - - - - - -
+ * constructors
+ * - - - - - - - - -
+ */
+
+#if($helper.isServer($mc))
+/**
+ * new_$helper.getStubName($intf, $suffix)()
+ * @param p serv factory parameter bundle, caller retains.
+ *
+ * this constructor is called on the server via callback from the listener
+ * socket accept handler <transportfactory>_session_accepted, via the new_server
+ * function pointer to perf_helper.new_helper_accepted_server().
+ * java binding passes this constructor the delivery service, however we pass the
+ * etch_server_factory parameter bundle, (i_sessionlistener.server_params),
+ * i_sessionlistener being the set session interface of etch_tcp_server.
+ */
+$helper.getStubName($intf, $suffix)* new_$helper.getStubName($intf, $suffix)(etch_server_factory* p, etch_session* session)
+{
+ i_delivery_service* ids = session->ds;
+ etch_threadpool *qp = p->qpool, *fp = p->fpool;
+
+ $helper.getStubName($intf, $suffix)* mystub = new_serverstub_init (session->server,
+ sizeof($helper.getStubName($intf, $suffix)), destroy_$helper.getStubName($intf, $suffix), ids, qp, fp, p);
+
+ ((etch_object*)mystub)->class_id = get_dynamic_classid_unique(&CLASSID_$helper.getStubName($intf, $suffix).toUpperCase());
+ mystub->session_id = session->session_id;
+
+ /* initialize service-specific methods and data here. this facility may
+ * likely prove unecessary, as this is generated code, in which case we
+ * can remove any associated comments from code and headers. */
+ //mystub->my_example_obj = etch_malloc(128, 0); /* example custom alloc */
+
+ /* set stub "helper" methods (run() procedures for each message type) */
+#foreach( $n in $intf.iterator())
+#if ($n.isMessage())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+#if (!$n.isHidden())
+ etchtype_set_type_stubhelper($helper.getVfName($intf)_get_static()->_mt_$helper.getServiceName($intf)_$n.name(), $helper.getStubName($intf, '')_run_$helper.getDirectionName($mc)_$n.name());
+#end
+#end
+#end
+#end
+
+ return mystub;
+}
+#end
+#if($helper.isClient($mc))
+/**
+ * new_$helper.getStubName($intf, $suffix).
+ * called from $helper.getRemoteName($intf, $helper.getRemoteDirection($mc))* perfhelper.new_remote_$helper.getRemoteDirection($mc)().
+ * @param p client parameter bundle
+ */
+$helper.getStubName($intf, $suffix)* new_$helper.getStubName($intf, $suffix) (etch_client_factory* p)
+{
+ $helper.getStubName($intf, $suffix)* mystub = NULL;
+ i_delivery_service* ids = p->dsvc;
+ etch_threadpool *qp = p->qpool, *fp = p->fpool;
+
+ i_$helper.getBaseName($intf, $suffix)* client = p->iclient;
+ ETCH_ASSERT(is_etch_ideliverysvc(ids));
+ ETCH_ASSERT(is_etch_client_base(client));
+
+ mystub = new_clientstub_init (client, sizeof($helper.getStubName($intf, $suffix)),
+ destroy_$helper.getStubName($intf, $suffix), ids, qp, fp, p);
+
+ ((etch_object*)mystub)->class_id = get_dynamic_classid_unique(&CLASSID_$helper.getStubName($intf, $suffix).toUpperCase());
+ mystub->server_id = p->server_id;
+
+ /* initialize custom methods and data here */
+
+ /* set stub helper methods */
+#foreach( $n in $intf.iterator())
+#if ($n.isMessage())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+#if (!$n.isHidden())
+ etchtype_set_type_stubhelper($helper.getVfName($intf)_get_static()->_mt_$helper.getServiceName($intf)_$n.name(), $helper.getStubName($intf, '')_run_$helper.getDirectionName($mc)_$n.name());
+#end
+#end
+#end
+#end
+
+ return mystub;
+}
+#end
+
+/**
+ * is_$helper.getStubName($intf, $suffix)()
+ */
+int is_$helper.getStubName($intf, $suffix)(void* obj)
+{
+ return obj && ((etch_object*)obj)->class_id == CLASSID_$helper.getStubName($intf, $suffix).toUpperCase();
+}
+
+/**
+ * destroy_def_$helper.getStubName($intf, $suffix)()
+ * $helper.getStubName($intf, $suffix) user-allocated memory destructor.
+ * called back from private object destructor destroy_stub_object().
+ * if you explicitly allocate memory in the client stub object, destroy it here.
+ */
+int destroy_$helper.getStubName($intf, $suffix)(void* data)
+{
+ /*
+ $helper.getStubName($intf, $suffix)* mystub = ($helper.getStubName($intf, $suffix)*)data;
+ free custom memory allocations here */
+ //etch_free(mystub->my_example_obj); /* free example alloc */
+
+ return 0;
+}
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/stub_h.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/stub_h.vm
new file mode 100644
index 0000000..cfd6ece
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/stub_h.vm
@@ -0,0 +1,92 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#ifndef $helper.getStubName($intf, $suffix).toUpperCase()_H
+#define $helper.getStubName($intf, $suffix).toUpperCase()_H
+
+\#include "etch_stub.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_$helper.getStubName($intf, $suffix).toUpperCase();
+
+/**
+ * $helper.getStubName($intf, $suffix)
+ */
+typedef struct $helper.getStubName($intf, $suffix)
+{
+ etch_object object;
+
+ etch_stub* stub_base;
+
+#if( $helper.getDirectionName($mc).equals("client"))
+ int server_id;
+#end
+#if(! $helper.getDirectionName($mc).equals("client"))
+ int session_id; /* client session to which stub belongs */
+#end
+ int (*destroyex) (void*); /* user memory destructor */
+
+ /* - - - - - - - - - - - - - - - - -
+ * $mc.toString().toLowerCase()_directed service virtuals
+ * - - - - - - - - - - - - - - - - -
+ */
+
+#foreach( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if ($n.isMsgDir($mc) || $n.isMsgDirBoth())
+#if (!$n.isHidden())
+## add server direction here
+#end
+#end
+#end
+#end
+#if (!$intf.hasMessageDirection($mc) && $helper.hasMessageDirectionBoth( $intf ))
+ /* no $mc.toString().toLowerCase()-directed items defined */
+#end
+
+ /* - - - - - - - - - - - - - - - - -
+ * service-specific allocations
+ * - - - - - - - - - - - - - - - - -
+ */
+ // add here
+
+} $helper.getStubName($intf, $suffix);
+
+#if($helper.isServer($mc))
+$helper.getStubName($intf, $suffix)* new_$helper.getStubName($intf, $suffix) (etch_server_factory*, etch_session*);
+#else
+$helper.getStubName($intf, $suffix)* new_$helper.getStubName($intf, $suffix) (etch_client_factory*);
+#end
+int is_$helper.getStubName($intf, $suffix)(void* obj);
+int destroy_$helper.getStubName($intf, $suffix) (void*);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* PERF_SERVER_STUB_H */
+
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/vf_c.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/vf_c.vm
new file mode 100644
index 0000000..d73b5ff
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/vf_c.vm
@@ -0,0 +1,679 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getVfFileNameI($intf)
+ * $helper.getServiceName($intf) service value factory
+ */
+
+\#include "$helper.getVfFileNameH($intf)"
+\#include "$helper.getIntfFileNameH($intf)"
+\#include "etch_serializer.h"
+\#include "etch_exception.h"
+\#include "etch_objecttypes.h"
+\#include "etch_general.h"
+\#include "etch_map.h"
+\#include "etch_runtime.h"
+#foreach($serviceName in $helper.getUsedServiceNames($intf))
+\#include "${serviceName.toLowerCase()}.h"
+#end
+#foreach($serviceName in $helper.getUsedServiceNames($intf))
+\#include "${serviceName.toLowerCase()}_valufact.h"
+#end
+
+unsigned short CLASSID_$helper.getVfName($intf).toUpperCase()_IMPL;
+
+static $helper.getVfName($intf)_statics* _g_$helper.getVfName($intf)_statics = NULL;
+
+/* constructors */
+etch_arraylist* $helper.getVfName($intf)_get_types($helper.getVfName($intf)*);
+
+void $helper.getVfName($intf)_free_statics();
+
+/* initializers */
+static int $helper.getVfName($intf)_init_static_fields();
+static int $helper.getVfName($intf)_init_static_types();
+static int $helper.getVfName($intf)_init_static_parameters();
+static int $helper.getVfName($intf)_init_static_serializers();
+
+#foreach($n in $intf.iterator())
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+/* $n.name() serializer */
+etch_serializer* new_$n.efqname($helper)_serializer(etch_type*, etch_field*);
+etch_object* etchserializer_$n.efqname($helper)_export_value(etch_serializer*, etch_object*);
+etch_object* etchserializer_$n.efqname($helper)_import_value(etch_serializer*, etch_object*);
+#end
+#end
+
+#set($valufactname = $helper.getVfName($intf))
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * static constructors/destructors
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_status_t $helper.getServiceName($intf)_etch_runtime_shutdown_hook_func()
+{
+ if(_g_$helper.getServiceName($intf)_valufact_statics) {
+ $helper.getServiceName($intf)_valufact_free_statics();
+ }
+ return ETCH_SUCCESS;
+}
+
+${valufactname}_statics* ${valufactname}_get_static(){
+ if(_g_${valufactname}_statics == NULL){
+ _g_${valufactname}_statics = malloc(sizeof(${valufactname}_statics));
+ memset(_g_${valufactname}_statics ,0 ,sizeof(${valufactname}_statics));
+
+ _g_${valufactname}_statics->_etch_${valufactname}_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ _g_${valufactname}_statics->_etch_${valufactname}_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+
+ defvf_initialize_static(_g_${valufactname}_statics->_etch_${valufactname}_typemap, _g_${valufactname}_statics->_etch_${valufactname}_c2tmap);
+ ${valufactname}_init_static_types();
+ ${valufactname}_init_static_fields();
+ ${valufactname}_init_static_parameters();
+ ${valufactname}_init_static_serializers();
+ etch_runtime_shutdown_hook_add($helper.getServiceName($intf)_etch_runtime_shutdown_hook_func);
+ }
+
+ return _g_${valufactname}_statics;
+}
+
+
+/**
+ * $helper.getVfName($intf)_init_types()
+ * instantiate type objects
+ */
+static int $helper.getVfName($intf)_init_static_types ()
+{
+ int restype = NULL;
+ struct i_hashtable* vtab = NULL;
+ $helper.getVfName($intf)_statics* p = $helper.getVfName($intf)_get_static();
+
+ /* instantiate type name strings */
+
+## generate messages
+#foreach($n in $intf.iterator() )
+#if($n.isMessage())
+#if(!$n.isHidden())
+ p->str_$helper.getServiceName($intf)_$n.name() = new_wchar(L"$intf.fqname().$n.name()");
+#if(! $n.isOneway())
+ p->str_$helper.getServiceName($intf)__result_$n.name() = new_wchar(L"$intf.fqname()._result_$n.name()");
+#end
+#end
+#end
+#end
+
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+ p->str_$n.efqname($helper) = new_wchar(L"$n.fqname()");
+#end
+#end
+
+ /* instantiate type objects */
+#foreach($n in $intf.iterator() )
+#if($n.isMessage())
+#if(!$n.isHidden())
+ p->_mt_$helper.getServiceName($intf)_$n.name() = new_static_type(p->str_$helper.getServiceName($intf)_$n.name());
+ ETCH_ASSERT(p->_mt_$helper.getServiceName($intf)_$n.name());
+#if(! $n.isOneway())
+ p->_mt_$helper.getServiceName($intf)__result_$n.name() = new_static_type(p->str_$helper.getServiceName($intf)__result_$n.name());
+ ETCH_ASSERT(p->_mt_$helper.getServiceName($intf)__result_$n.name());
+#end
+#end
+#end
+#end
+
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+ p->_mt_$n.efqname($helper) = new_static_type(p->str_$n.efqname($helper));
+ ETCH_ASSERT(p->_mt_$n.efqname($helper));
+#end
+#end
+ vtab = (struct i_hashtable*)((etch_object*)p->_etch_$helper.getVfName($intf)_typemap)->vtab;
+ /* add types to vf */
+#foreach($n in $intf.iterator() )
+#if($n.isMessage())
+#if(!$n.isHidden())
+ restype = vtab->inserth(p->_etch_$helper.getVfName($intf)_typemap->realtable, p->_mt_$helper.getServiceName($intf)_$n.name(), NULL, p->_etch_$helper.getVfName($intf)_typemap, 0);
+ ETCH_ASSERT(! restype);
+#if(! $n.isOneway())
+ restype = vtab->inserth(p->_etch_$helper.getVfName($intf)_typemap->realtable, p->_mt_$helper.getServiceName($intf)__result_$n.name(), NULL, p->_etch_$helper.getVfName($intf)_typemap, 0);
+ ETCH_ASSERT(! restype);
+#end
+#end
+#end
+#end
+
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+ restype = vtab->inserth(p->_etch_$helper.getVfName($intf)_typemap->realtable, p->_mt_$n.efqname($helper), NULL, p->_etch_$helper.getVfName($intf)_typemap, 0);
+ ETCH_ASSERT(! restype);
+#end
+#end
+
+ /* set type response fields */
+#foreach($n in $intf.iterator() )
+#if($n.isMessage())
+#if(!$n.isHidden())
+#if(!$n.isOneway())
+ etchtype_set_response_field(p->_mt_$helper.getServiceName($intf)__result_$n.name(), builtins._mf_result);
+#end
+#end
+#end
+#end
+
+ /* set message result types */
+#foreach($n in $intf.iterator() )
+#if($n.isMessage())
+#if(!$n.isHidden())
+#if(!$n.isOneway())
+ etchtype_set_result_type (p->_mt_$helper.getServiceName($intf)_$n.name(), p->_mt_$helper.getServiceName($intf)__result_$n.name());
+#end
+#end
+#end
+#end
+
+ /* set timeouts */
+#foreach($n in $intf.iterator() )
+#if($n.isMessage())
+#if(!$n.isHidden())
+#if(!$n.isOneway())
+ etchtype_set_timeout (p->_mt_$helper.getServiceName($intf)__result_$n.name(), $n.getTimeout());
+#end
+#end
+#end
+#end
+
+ /* set async modes */
+#foreach ( $n in $intf.iterator() )
+#if ($n.isMessage())
+#if (!$n.isHidden())
+#if ($n.isQueuedAsyncReceiver())
+ etchtype_set_async_mode(p->_mt_$helper.getServiceName($intf)_$n.name(), ETCH_ASYNCMODE_QUEUED);
+#elseif ($n.isFreeAsyncReceiver())
+ etchtype_set_async_mode(p->_mt_$helper.getServiceName($intf)_$n.name(), ETCH_ASYNCMODE_FREE);
+#else
+ etchtype_set_async_mode(p->_mt_$helper.getServiceName($intf)_$n.name(), ETCH_ASYNCMODE_NONE);
+#end
+#end
+#end
+#end
+
+ return 0;
+}
+
+
+/**
+ * $helper.getVfName($intf)_init_fields()
+ * instantiate field objects
+ */
+static int $helper.getVfName($intf)_init_static_fields ()
+{
+#set ($tmp = $helper.resetHistory())
+#foreach($n in $intf.iterator())
+#if(!$n.isHidden())
+#if(!$n.isBuiltin())
+#foreach($p in $n.iterator())
+#if(!$helper.historyContains($p.name().toString()))
+ $helper.getVfName($intf)_get_static()->str_$helper.getServiceName($intf)_$p.name() = new_wchar(L"$p.name()");
+#set ($tmp = $helper.addStringToHistory($p.name().toString()))
+#end
+#end
+#end
+#end
+#end
+
+#set ($tmp = $helper.resetHistory())
+#foreach($n in $intf.iterator())
+#if(!$n.isHidden())
+#if(!$n.isBuiltin())
+#foreach($p in $n.iterator())
+#if(!$helper.historyContains($p.name().toString()))
+ $helper.getVfName($intf)_get_static()->_mf_$helper.getServiceName($intf)_$p.name() = new_static_field($helper.getVfName($intf)_get_static()->str_$helper.getServiceName($intf)_$p.name());
+#set ($tmp = $helper.addStringToHistory($p.name().toString()))
+#end
+#end
+#end
+#end
+#end
+ return 0;
+
+}
+
+/**
+ * $helper.getVfName($intf)_init_parameters()
+ * initialize service method parameters
+ */
+static int $helper.getVfName($intf)_init_static_parameters ()
+{
+ $helper.getVfName($intf)_statics* p = $helper.getVfName($intf)_get_static();
+
+
+#foreach($n in $intf.iterator())
+#if($n.isEnumx())
+#foreach($p in $n.iterator())
+ etchtype_put_validator(p->_mt_$helper.getServiceName($intf)_$n.name(),
+ clone_field(p->_mf_$helper.getServiceName($intf)_$p.name()), (etch_object*)etchvtor_boolean_get(0));
+#end
+#elseif($n.isStruct() || $n.isExcept())
+//params for $n
+#foreach( $p in $n.getAllParameters() )
+ etchtype_put_validator(p->_mt_$helper.getServiceName($intf)_$n.name(),
+ clone_field(p->_mf_$helper.getServiceName($intf)_$p.fqname()),
+ $helper.getValidator($intf, $p));
+#end
+#else
+//params for $n
+#foreach( $p in $n.iterator() )
+#if($n.isHidden())
+ etchtype_put_validator(p->_mt_$helper.getServiceName($intf)_$n.name(),
+ builtins._mf_$p.fqname(),
+ $helper.getValidator($intf, $p));
+#else
+ etchtype_put_validator(p->_mt_$helper.getServiceName($intf)_$n.name(),
+ clone_field(p->_mf_$helper.getServiceName($intf)_$p.fqname()),
+ $helper.getValidator($intf, $p));
+#end
+#end
+#end
+#if ($n.isMessage())
+ etchtype_put_validator(p->_mt_$helper.getServiceName($intf)_$n.name(),
+ clone_field(builtins._mf__message_id), (etch_object*) etchvtor_int64_get(0));
+#if ($n.isHidden())
+ etchtype_put_validator(p->_mt_$helper.getServiceName($intf)_$n.name(),
+ clone_field(builtins._mf_result), (etch_object*) etchvtor_exception_get(0));
+ etchtype_put_validator(p->_mt_$helper.getServiceName($intf)_$n.name(),
+ clone_field(builtins._mf__in_reply_to), (etch_object*) etchvtor_int64_get(0));
+#set( $param = $n.getResultParam() )
+#set( $reqMsg = $n.getRequestMessage() )
+#foreach( $ex in $reqMsg.thrown().iterator() )
+ etchtype_put_validator(p->_mt_$helper.getServiceName($intf)_$n.name(),
+ builtins._mf_result,
+ $helper.getValidator($intf, $ex));
+#end
+ etchtype_put_validator(p->_mt_$helper.getServiceName($intf)_$n.name(),
+ builtins._mf_result,
+ $helper.getValidator($intf, $param));
+#end
+#end
+#end
+
+
+//OLD
+//structs
+ return 0;
+}
+
+/**
+ * $helper.getVfName($intf)_init_serializers()
+ */
+static int $helper.getVfName($intf)_init_static_serializers ()
+{
+ int result = 0;
+#foreach($n in $intf.iterator())
+#if ($n.isExcept())
+#set( $c2map_needed = true )
+#elseif ($n.isStruct() || $n.isEnumx())
+#set( $c2map_needed = true )
+#end
+#end
+#if ($c2map_needed)
+ $helper.getVfName($intf)_statics* p = $helper.getVfName($intf)_get_static();
+ class_to_type_map* c2tmap = p->_etch_$helper.getVfName($intf)_c2tmap;
+#end
+
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+ const unsigned short classid_$n.efqname($helper) = get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase());
+#end
+#end
+
+ /* note that etch_serializer_init takes care of the class to type, setting
+ * of component type, instantiation of import export helper and installing
+ * it to the type.
+ */
+
+#foreach($n in $intf.iterator() )
+#if($n.isExcept())
+ /* serializer for $n.efqname($helper) */
+ result = etch_serializer_init (p->_mt_$n.efqname($helper), p->str_$n.efqname($helper),
+ ETCHMAKECLASS(ETCHTYPEB_EXCEPTION, classid_$n.efqname($helper)),
+ c2tmap, NULL, new_$n.efqname($helper)_serializer);
+
+#elseif ($n.isStruct() || $n.isEnumx())
+ /* serializer for $n.efqname($helper) */
+ result = etch_serializer_init (p->_mt_$n.efqname($helper), p->str_$n.efqname($helper),
+ ETCHMAKECLASS(ETCHTYPEB_USER, classid_$n.efqname($helper)),
+ c2tmap, NULL, new_$n.efqname($helper)_serializer);
+
+#end
+#end
+ return result;
+}
+
+
+
+/**
+ * destroy_$helper.getVfName($intf)_impl()
+ * destructor for the $helper.getServiceName($intf) value factory extension
+ */
+int destroy_$helper.getVfName($intf)_impl(void* data)
+{
+ $helper.getVfName($intf)_impl* impl = ($helper.getVfName($intf)_impl*)data;
+ if (NULL == impl) return -1;
+
+ if (!is_etchobj_static_content(impl))
+ {
+ // add if neccessary
+ }
+
+ return destroy_objectex((etch_object*) impl);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * constructors/destructors
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+$helper.getVfName($intf)* new_$helper.getVfName($intf)()
+{
+ $helper.getVfName($intf)_impl* impl = NULL;
+ default_value_factory* pvf = new_default_value_factory (
+ $helper.getVfName($intf)_get_static()->_etch_$helper.getVfName($intf)_typemap,
+ $helper.getVfName($intf)_get_static()->_etch_$helper.getVfName($intf)_c2tmap);
+ ETCH_ASSERT(pvf);
+
+ /* note that the vf destructor is the default value factory destructor,
+ * which in turn invokes the destructor on its impl object
+ */
+ impl = ($helper.getVfName($intf)_impl*) new_object (sizeof($helper.getVfName($intf)_impl),
+ ETCHTYPEB_VALUEFACTIMP, get_dynamic_classid_unique(&CLASSID_$helper.getVfName($intf).toUpperCase()_IMPL));
+
+ ((etch_object*)impl)->destroy = destroy_$helper.getVfName($intf)_impl;
+ pvf->impl = (etch_object*) impl;
+#foreach ( $n in $intf.iterator() )
+#if ($n.isMixin())
+#set( $m = $n.getModule() )
+#set( $s = $m.iterator().next() )
+ defvf_add_mixin(pvf, new_$s.name().toString().toLowerCase()_valufact());
+#end
+#end
+ return pvf;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * vf class methods
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+void $helper.getVfName($intf)_free_statics ()
+{
+
+ $helper.getVfName($intf)_statics* data = $helper.getVfName($intf)_get_static();
+ ETCH_ASSERT(data);
+
+#foreach($n in $intf.iterator() )
+#if($n.isMessage())
+#if(!$n.isHidden())
+ destroy_static_type (data->_mt_$helper.getServiceName($intf)_$n.name());
+#if(! $n.isOneway())
+ destroy_static_type (data->_mt_$helper.getServiceName($intf)__result_$n.name());
+#end
+#end
+#end
+#end
+
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isExcept() || $n.isEnumx())
+ destroy_static_type (data->_mt_$n.efqname($helper));
+#end
+#end
+
+#set ($tmp = $helper.resetHistory())
+#foreach($n in $intf.iterator())
+#if (!$n.isHidden())
+#foreach($p in $n.iterator())
+#if(!$helper.historyContains($p.name().toString()))
+ destroy_static_field(data->_mf_$helper.getServiceName($intf)_$p.name());
+#set ($tmp = $helper.addStringToHistory($p.name().toString()))
+#end
+#end
+##
+#end
+#end
+
+#set ($tmp = $helper.resetHistory())
+#foreach($n in $intf.iterator())
+#if (!$n.isHidden())
+#foreach($p in $n.iterator())
+#if(!$helper.historyContains($p.name().toString()))
+ etch_free (data->str_$helper.getServiceName($intf)_$p.name());
+#set ($tmp = $helper.addStringToHistory($p.name().toString()))
+#end
+#end
+##
+#end
+#end
+
+#foreach($n in $intf.iterator())
+#if($n.isMessage())
+#if(!$n.isHidden())
+ etch_free (data->str_$helper.getServiceName($intf)_$n.name());
+#if(! $n.isOneway())
+ etch_free (data->str_$helper.getServiceName($intf)__result_$n.name());
+#end
+##
+#end
+#end
+#end
+
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isExcept() || $n.isEnumx())
+ etch_free (data->str_$n.efqname($helper));
+#end
+#end
+
+
+ //set_etchobj_static_content(data->_etch_$helper.getServiceName($intf)_valufact_typemap);
+ //set_etchobj_static_content(data->_etch_$helper.getServiceName($intf)_valufact_c2tmap);
+
+ data->_etch_$helper.getServiceName($intf)_valufact_typemap->is_readonly_keys = 0;
+ data->_etch_$helper.getServiceName($intf)_valufact_c2tmap->is_readonly_keys = 0;
+ etch_object_destroy(data->_etch_$helper.getServiceName($intf)_valufact_typemap);
+ etch_object_destroy(data->_etch_$helper.getServiceName($intf)_valufact_c2tmap);
+
+ etch_free(data);
+ _g_$helper.getServiceName($intf)_valufact_statics = NULL;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * serializers
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+/**
+ * etchserializer_$n.efqname($helper)_export_value()
+ * export valueof a $n.efqname($helper)
+ * @param objval a $n.efqname($helper), caller owns it and presumably will
+ * destroy it upon return from this method.
+ * @return the exported disposable structvalue object. caller must cast it.
+ */
+etch_object* etchserializer_$n.efqname($helper)_export_value(etch_serializer* thisx, etch_object* objval)
+{
+ const int THISINITSIZE = 2;
+ etch_structvalue* expstruct = NULL;
+ const unsigned short classid_$n.efqname($helper) = get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase());
+#if($n.isExcept())
+ if (!is_etch_objparams(objval, ETCHTYPEB_EXCEPTION, classid_$n.efqname($helper))) return NULL;
+#else
+ if (!is_etch_objparams(objval, ETCHTYPEB_USER, classid_$n.efqname($helper))) return NULL;
+#end
+
+ expstruct = new_structvalue((etch_type*) thisx->type, THISINITSIZE);
+
+#if($n.isEnumx())
+ switch((($n.efqname($helper)*)objval)->value)
+ {
+#foreach($enumType in $n.iterator())
+ case $n.name()_$enumType: structvalue_put(expstruct, clone_field(_g_$helper.getVfName($intf)_statics->_mf_$helper.getServiceName($intf)_$enumType), (etch_object*) new_boolean(TRUE)); break;
+#end
+ }
+#end
+
+#foreach($p in $n.getAllParameters())
+#if($p.type().isArray())
+ structvalue_put(expstruct, _g_$helper.getVfName($intf)_statics->_mf_$helper.getServiceName($intf)_$p.name(),
+ etch_object_clone_func((void*)(($n.efqname($helper)*)objval)->${p.fqname()}));
+#elseif($helper.isEnumParam($p))
+ structvalue_put(expstruct, _g_$helper.getVfName($intf)_statics->_mf_$helper.getServiceName($intf)_$p.name(),
+ (etch_object*)new_$helper.getIntfName( $intf )_$p.type()_init((($n.efqname($helper)*)objval)->${p.fqname()}));
+#elseif($helper.getValidatorStringForParam($p) == 'object'
+ || $helper.getValidatorStringForParam($p) == 'string')
+ if((($n.efqname($helper)*)objval)->${p.fqname()}) {
+ structvalue_put(expstruct, _g_$helper.getVfName($intf)_statics->_mf_$helper.getServiceName($intf)_$p.name(),
+ etch_object_clone_func((void*)(($n.efqname($helper)*)objval)->${p.fqname()}));
+ }
+#else
+ structvalue_put(expstruct, _g_$helper.getVfName($intf)_statics->_mf_$helper.getServiceName($intf)_$p.name(),
+ (etch_object*) new_$helper.getValidatorStringForParam($p)((($n.efqname($helper)*)objval)->$p.fqname()));
+#end
+#end
+ return (etch_object*) expstruct; /* caller owns this structvalue */
+}
+
+/**
+ * etchserializer_$n.efqname($helper)_import_value()
+ * import value for a $n.efqname($helper).
+ * @param objval an etch_structvalue of appropriate type for the $n.efqname($helper).
+ * caller retains ownership of this object as with all imports.
+ * @return an opaque etch object containing the imported $n.efqname($helper).
+ * caller owns and must destroy the returned object.
+ */
+etch_object* etchserializer_$n.efqname($helper)_import_value (etch_serializer* thisx, etch_object* objval)
+{
+ $n.efqname($helper)* outobj = NULL;
+
+#foreach($p in $n.getAllParameters())
+ $helper.getEtchTypeName($p.type())* valobj_$p.name() = NULL;
+#end
+
+#if($n.isEnumx())
+ $helper.getIntfName( $intf )_$n.name()_enum enumValue = NULL;
+ etch_boolean* enumFlagValue;
+#end
+
+ etch_structvalue* instruct = NULL;
+ if (!is_etch_struct(objval)) return NULL;
+
+ instruct = (etch_structvalue*) objval;
+ if (!structvalue_is_type(instruct, thisx->type)) return NULL;
+
+#if($n.isEnumx())
+
+#foreach($enumType in $n.iterator())
+ enumFlagValue = (etch_boolean*) structvalue_get(instruct, _g_$helper.getVfName($intf)_statics->_mf_$helper.getServiceName($intf)_$enumType);
+ if(enumFlagValue && enumFlagValue->value)
+ {
+ enumValue = $n.name()_$enumType;
+ }
+#end
+
+#end
+
+#foreach($p in $n.getAllParameters())
+ /* fetch the $p.name() value wrapper out of the struct. struct owns it */
+#if($helper.isEnumParam($p))
+ valobj_$p.name() = ($helper.getEtchTypeName($p.type())*) structvalue_get(instruct, _g_$helper.getVfName($intf)_statics->_mf_$helper.getServiceName($intf)_$p.name());
+#elseif($p.type().isArray())
+ valobj_$p.name() = (etch_arraytype*) structvalue_remove(instruct, _g_$helper.getVfName($intf)_statics->_mf_$helper.getServiceName($intf)_$p.name());
+#elseif($helper.getValidatorStringForParam($p) == 'object')
+ valobj_$p.name() = ($helper.getEtchTypeName($p.type())*) structvalue_get(instruct, _g_$helper.getVfName($intf)_statics->_mf_$helper.getServiceName($intf)_$p.name());
+#else
+ valobj_$p.name() = ($helper.getEtchTypeName($p.type())*) structvalue_get(instruct, _g_$helper.getVfName($intf)_statics->_mf_$helper.getServiceName($intf)_$p.name());
+#end
+#if($p.type().isArray())
+ if (valobj_$p.name() && !is_etch_arraytype(valobj_$p.name())) return NULL;
+#elseif($p.type().isObject())
+ //nothing to check, is object
+#else
+ if (valobj_$p.name() && !is_$helper.getEtchTypeName($p.type())(valobj_$p.name())) return NULL;
+#end
+#end
+
+ outobj = new_$n.efqname($helper)();
+
+#if($n.isEnumx())
+ outobj->value = enumValue;
+#end
+
+#foreach($p in $n.getAllParameters())
+#if($p.type().isArray())
+ if(valobj_$p.name())
+ outobj->$p.name() = valobj_$p.name();
+ //outobj->$p.name() = (etch_arraytype*) etch_object_clone_func((void*)valobj_$p.name());
+#elseif($helper.getValidatorStringForParam($p) == 'string')
+ if(valobj_$p.name())
+ outobj->$p.name() = (etch_string*) etch_object_clone_func((void*)valobj_$p.name());
+#elseif($helper.isEnumParam($p))
+ if(valobj_$p.name())
+ outobj->$p.name() = valobj_$p.name()->value;
+#elseif($helper.getValidatorStringForParam($p) == 'object')
+ if(valobj_$p.name())
+ outobj->$p.name() = ($helper.getEtchTypeName($p.type())*) etch_object_clone_func((void*)valobj_$p.name());
+#else
+ if(valobj_$p.name())
+ outobj->$p.name() = valobj_$p.name()->value;
+#end
+#end
+ return (etch_object*) outobj; /* caller owns this object */
+}
+
+/**
+ * new_$n.efqname($helper)_serializer()
+ * etch_serializer_excp constructor - conforms to typedef etch_serializer_ctor
+ * @param type - not owned
+ * @param field - not owned
+ */
+etch_serializer* new_$n.efqname($helper)_serializer(etch_type* type, etch_field* field)
+{
+ etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);
+
+ ((etch_object*)newobj)->class_id = get_dynamic_classid_unique(&CLASSID_$n.efqname($helper).toUpperCase()_SERIALIZER);
+ newobj->type = type; /* not owned */
+ newobj->field = field; /* not owned */
+
+ newobj->export_value = etchserializer_$n.efqname($helper)_export_value;
+ newobj->import_value = etchserializer_$n.efqname($helper)_import_value;
+
+ return newobj;
+}
+
+#end
+#end
diff --git a/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/vf_h.vm b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/vf_h.vm
new file mode 100644
index 0000000..91c6820
--- /dev/null
+++ b/binding-c/compiler/src/main/resources/org/apache/etch/bindings/c/compiler/vf_h.vm
@@ -0,0 +1,143 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+
+/*
+ * $helper.getVfFileNameH($intf)
+ * $helper.getIntfName($intf) implementation of value factory
+ */
+
+#ifndef $helper.getVfName($intf).toUpperCase()_H
+#define $helper.getVfName($intf).toUpperCase()_H
+
+\#include "etch_default_value_factory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef default_value_factory $helper.getVfName($intf).toLowerCase();
+
+extern unsigned short CLASSID_$helper.getVfName($intf).toUpperCase()_IMPL;
+
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+unsigned short CLASSID_$helper.getServiceName($intf).toUpperCase()_$n.name().name().toUpperCase()_SERIALIZER;
+#end
+#end
+
+
+/**
+ * $helper.getVfName($intf)_statics
+ */
+typedef struct $helper.getVfName($intf)_statics
+{
+ class_to_type_map* _etch_$helper.getVfName($intf)_c2tmap;
+ vf_idname_map* _etch_$helper.getVfName($intf)_typemap;
+
+## generate messages
+#foreach($n in $intf.iterator() )
+#if($n.isMessage())
+#if(!$n.isHidden())
+ etch_type* _mt_$helper.getServiceName($intf)_$n.name();
+#if(! $n.isOneway())
+ etch_type* _mt_$helper.getServiceName($intf)__result_$n.name();
+#end
+#end
+#end
+#end
+
+## generate stucts
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+ etch_type* _mt_$helper.getServiceName($intf)_$n.name();
+#end
+#end
+
+
+#set ($tmp = $helper.resetHistory())
+#foreach($n in $intf.iterator())
+#if (!$n.isHidden())
+#foreach($p in $n.iterator())
+#if(!$helper.historyContains($p.name().toString()))
+ etch_field* _mf_$helper.getServiceName($intf)_$p.name();
+#set ($tmp = $helper.addStringToHistory($p.name().toString()))
+#end
+#end
+##
+#end
+#end
+
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+ struct etch_serializer* serializer_$n.name();
+#end
+#end
+
+#set ($tmp = $helper.resetHistory())
+#foreach($n in $intf.iterator())
+#if (!$n.isHidden())
+#foreach($p in $n.iterator())
+#if(!$helper.historyContains($p.name().toString()))
+ wchar_t* str_$helper.getServiceName($intf)_$p.name();
+#set ($tmp = $helper.addStringToHistory($p.name().toString()))
+#end
+#end
+##
+#end
+#end
+
+#foreach($n in $intf.iterator() )
+#if($n.isMessage())
+ wchar_t* str_$helper.getServiceName($intf)_$n.name();
+#end
+#end
+##generate struct names
+#foreach($n in $intf.iterator() )
+#if ($n.isStruct() || $n.isEnumx() || $n.isExcept())
+ wchar_t* str_$helper.getIntfName($intf)_$n.name();
+#end
+#end
+
+} $helper.getVfName($intf)_statics;
+
+/**
+ * $helper.getVfName($intf)_impl
+ * $helper.getIntfName($intf) extension of default value factory
+ */
+typedef struct $helper.getVfName($intf)_impl
+{
+ etch_object object;
+
+} $helper.getVfName($intf)_impl;
+
+$helper.getVfName($intf)* new_$helper.getVfName($intf)();
+$helper.getVfName($intf)_statics* $helper.getVfName($intf)_get_static();
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif
+
diff --git a/binding-c/runtime/c/README.win32 b/binding-c/runtime/c/README.win32
new file mode 100644
index 0000000..55c3d6f
--- /dev/null
+++ b/binding-c/runtime/c/README.win32
@@ -0,0 +1,75 @@
+How to build the etch c binding:
+*******************************
+
+You need the following:
+***********************
+Build for Win32
+- Apache APR Source Version 1.3 for Win32, e.g. available under
+ http://ftp-stud.hs-esslingen.de/pub/Mirrors/ftp.apache.org/dist/apr/apr-1.3.9-win32-src.zip or
+ http://archive.apache.org/dist/apr/apr-1.3.9-win32-src.zip
+
+- Apache APR Iconv Source Version 1.2 for Win32, e.g. available under
+ http://ftp-stud.hs-esslingen.de/pub/Mirrors/ftp.apache.org/dist/apr/apr-iconv-1.2.1-win32-src-r2.zip or
+ http://archive.apache.org/dist/apr/apr-iconv-1.2.1-win32-src-r2.zip
+
+- Apache APR Util Version 1.3 for Win32, e.g. available under
+ ftp://ftp-stud.hs-esslingen.de/pub/Mirrors/ftp.apache.org/dist/apr/apr-util-1.3.9-win32-src.zip or
+ http://archive.apache.org/dist/apr/apr-util-1.3.9-win32-src.zip
+
+- CUnit Sources from Version CUnit-2.1-0-src.zip
+ http://cunit.sourceforge.net/
+ download from http://sourceforge.net/projects/cunit/
+
+---------------------------------------------------------------------------------------------------------
+
+The following instructions were tested using MS Visual Studio 2005 SP1 on Windows 7.
+It should work with newer Visual Studio versions too, but was not tested.
+
+(Linux build will be available soon.)
+
+Prerequisites:
+**************
+
+- you should have the following folder structure inside your etch sources
+ - [somefolder]/etch/src/extern/apr
+ - [somefolder]/etch/src/extern/apr/apr
+ - [somefolder]/etch/src/extern/apr/apr-iconv
+ - [somefolder]/etch/src/extern/apr/apr-util
+ - [somefolder]/etch/src/extern/cunit
+
+- unpack all apr zips (apr-1.3.9-win32-src.zip, apr-iconv-1.2.1-win32-src-r2.zip, apr-util-1.3.9-win32-src.zip)
+ to [somefolder]/etch/src/extern/apr and remove all version numbers from the unpacked folders
+-- [somefolder]/etch/src/extern/apr/apr-1.3.9 to [somefolder]/etch/src/extern/apr/apr
+-- [somefolder]/etch/src/extern/apr/apr-iconv-1.2.1 to [somefolder]/etch/src/extern/apr/apr-iconv
+-- [somefolder]/etch/src/extern/apr/apr-util-1.3.9 to [somefolder]/etch/src/extern/apr/apr-util
+- unpack the CUnit-2.1-0-src.zip into the cunit folder and remove all version numbers from the unpacked folder
+-- [somefolder]/etch/src/extern/CUnit-2.1-0 to [somefolder]/etch/src/extern/apr/cunit
+
+To build, do the following:
+***************************
+
+1.) open the APR-util VS solution ([somefolder]/apr/apr-util/apr-util.dsw)
+ (convert to 2005 is ok, yes to all)
+ -> build the apr project
+ -> build the apriconv project
+ -> build the libapr project
+ -> build the libapriconv project
+ -> build the libapriconv_ccs_modules project
+ -> build the libapriconv_ces_modules project
+ the rest of the projects in the apr solution should not be needed
+2.) set the environment variable
+ APR_ICONV_PATH to [somefolder]\etch\src\extern\apr\apr-iconv\Debug\iconv
+3.) open the CUNIT solution in VS ([somefolder]/etch/src/extern/cunit/VS8/CUnit.sln)
+ -> build the libcunit project
+ the rest of the projects in the CUnit solution should not be needed
+4.) open the etch C binding solution ([somefolder]/etch/binding-c/runtime/c/etch-c.sln)
+ -> build the solution
+5.) copy
+ apr\Debug\libapr-1.dll
+ to
+ [somefolder]/etch/binding-c/runtime/src/test/target/win32/Debug
+5.) copy
+ apr-iconv\Debug\libapriconv-1.dll
+ to
+ [somefolder]/etch/binding-c/runtime/src/test/target/win32/Debug
+6.) run the project etch-c-test to verify your build
diff --git a/binding-c/runtime/c/etch-c.sln b/binding-c/runtime/c/etch-c.sln
new file mode 100644
index 0000000..c726809
--- /dev/null
+++ b/binding-c/runtime/c/etch-c.sln
@@ -0,0 +1,64 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "etch-c", "src\main\etch-c.vcproj", "{C49054D6-0122-4F20-A0A2-06197D08567B}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
+ ProjectSection(ProjectDependencies) = postProject
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66} = {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "etch-c-test", "src\test\etch-c-test.vcproj", "{64A36C3F-2BE4-4471-A291-C65BD8A9E6FE}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
+ ProjectSection(ProjectDependencies) = postProject
+ {C49054D6-0122-4F20-A0A2-06197D08567B} = {C49054D6-0122-4F20-A0A2-06197D08567B}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jenkhash", "src\extern\jenkhash\jenkhash.vcproj", "{CC75FED8-5D1B-4323-B73A-36C41FD1CC66}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug Static|Win32 = Debug Static|Win32
+ Release Static|Win32 = Release Static|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C49054D6-0122-4F20-A0A2-06197D08567B}.Debug Static|Win32.ActiveCfg = Debug Static|Win32
+ {C49054D6-0122-4F20-A0A2-06197D08567B}.Debug Static|Win32.Build.0 = Debug Static|Win32
+ {C49054D6-0122-4F20-A0A2-06197D08567B}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C49054D6-0122-4F20-A0A2-06197D08567B}.Debug|Win32.Build.0 = Debug|Win32
+ {C49054D6-0122-4F20-A0A2-06197D08567B}.Release Static|Win32.ActiveCfg = Release Static|Win32
+ {C49054D6-0122-4F20-A0A2-06197D08567B}.Release Static|Win32.Build.0 = Release Static|Win32
+ {C49054D6-0122-4F20-A0A2-06197D08567B}.Release|Win32.ActiveCfg = Release|Win32
+ {C49054D6-0122-4F20-A0A2-06197D08567B}.Release|Win32.Build.0 = Release|Win32
+ {64A36C3F-2BE4-4471-A291-C65BD8A9E6FE}.Debug Static|Win32.ActiveCfg = Debug Static|Win32
+ {64A36C3F-2BE4-4471-A291-C65BD8A9E6FE}.Debug Static|Win32.Build.0 = Debug Static|Win32
+ {64A36C3F-2BE4-4471-A291-C65BD8A9E6FE}.Debug|Win32.ActiveCfg = Debug|Win32
+ {64A36C3F-2BE4-4471-A291-C65BD8A9E6FE}.Debug|Win32.Build.0 = Debug|Win32
+ {64A36C3F-2BE4-4471-A291-C65BD8A9E6FE}.Release Static|Win32.ActiveCfg = Release Static|Win32
+ {64A36C3F-2BE4-4471-A291-C65BD8A9E6FE}.Release Static|Win32.Build.0 = Release Static|Win32
+ {64A36C3F-2BE4-4471-A291-C65BD8A9E6FE}.Release|Win32.ActiveCfg = Release|Win32
+ {64A36C3F-2BE4-4471-A291-C65BD8A9E6FE}.Release|Win32.Build.0 = Release|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Debug Static|Win32.ActiveCfg = Debug Static|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Debug Static|Win32.Build.0 = Debug Static|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Debug|Win32.Build.0 = Debug|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Release Static|Win32.ActiveCfg = Release Static|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Release Static|Win32.Build.0 = Release Static|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Release|Win32.ActiveCfg = Release|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/binding-c/runtime/c/include/etch.h b/binding-c/runtime/c/include/etch.h
new file mode 100644
index 0000000..facc18f
--- /dev/null
+++ b/binding-c/runtime/c/include/etch.h
@@ -0,0 +1,123 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ETCH_H_
+#define _ETCH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <apr_pools.h>
+#include <apr_atomic.h>
+#include <apr_errno.h>
+#include <apr_general.h>
+#include <apr_thread_mutex.h>
+#include <apr_thread_cond.h>
+#include <apr_portable.h>
+#include <apr_strings.h>
+#include <apr_time.h>
+
+/**
+ * macros
+ */
+#define ETCH_ASSERT(condition) assert(condition)
+#define ETCH_ASSERT_EXIT(condition) assert(condition)
+
+//TODO: check which component use this defines, change functions
+// to use etch_status_t
+#define ETCH_DEFSIZE 0
+#define ETCH_INFWAIT 0
+#define ETCH_NOWAIT (-1)
+#define ETCH_TIMEOUT 1
+#define ETCH_INTERRUPTED 0
+
+//TODO: check this stuff, check etch_nativearray encoding (QNX, Windows)
+#define ETCH_ENCODING_ASCII 0
+#define ETCH_ENCODING_UTF8 1
+#define ETCH_ENCODING_UTF16 2
+#define ETCH_ENCODING_UTF32 3
+#define ETCH_ENCODING_UCS2 4
+#define ETCH_ENCODING_UCS4 5
+#define ETCH_ENCODING_UNICODE 6
+
+#define ETCH_ENCODING_DEFAULT ETCH_ENCODING_UTF8
+#define IS_ETCH_ENCODING_8BIT(n) (n == ETCH_ENCODING_UTF8 || n == ETCH_ENCODING_ASCII)
+
+//TODO: check this sutff, etch_thread
+#define ETCH_THREADPOOLTYPE_FREE 0
+#define ETCH_THREADPOOLTYPE_QUEUED 1
+
+// TODO: check this stuff
+#define ETCHMAKECLASS(t,c) ((t << 16) | c)
+#define ETCHGETCLASS(n,t,c) do{ c=(short)(n & 0xffff); t=(short)(n>>16); }while(0);
+
+//TODO: check this stuff,etch_stub
+#define ETCH_ASYNCMODE_NONE 0 /* implies operation is synchronous */
+#define ETCH_ASYNCMODE_QUEUED 1 /* operation is queued to a thread pool */
+#define ETCH_ASYNCMODE_FREE 2 /* operation is executed in a new thread */
+
+//TODO: check, etch_tcpconxn
+#define ETCH_SHUTDOWNSIGNAL "$ETCHQUIT"
+
+/**
+ * common defines
+ */
+#undef NULL
+#define NULL 0
+
+#undef FALSE
+#define FALSE 0
+
+#undef TRUE
+#define TRUE 1
+
+/**
+ * typedefs of basic datatypes
+ */
+typedef char int8;
+typedef unsigned char uint8;
+typedef short int int16;
+typedef unsigned short int uint16;
+typedef int int32;
+typedef unsigned int uint32;
+typedef long long int64;
+typedef unsigned long long uint64;
+typedef unsigned char byte;
+typedef unsigned char boolean;
+
+uint32 etchhash(const void* key, const int keylen, const unsigned priorhash);
+
+typedef int (*etch_comparator) (void* myobj, void* otherobj);
+
+#define IS_ETCH_TRANSPORT_LITTLEENDIAN FALSE
+#define ETCHCONFIGDEF_EXAMPLE_STRING_MAXLEN 15
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_H */
diff --git a/binding-c/runtime/c/include/etch_arraylist.h b/binding-c/runtime/c/include/etch_arraylist.h
new file mode 100644
index 0000000..0f029a0
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_arraylist.h
@@ -0,0 +1,115 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_arrarylist.h
+ * arraylist implementation.
+ */
+
+#ifndef ETCHARRAYLIST_H
+#define ETCHARRAYLIST_H
+
+#include "etch.h"
+#include "etch_mutex.h"
+#include "etch_collection.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCHARRAYLIST_DEFSIZE 128
+#define ETCHARRAYLIST_CONTENT_SIMPLE 0 /* content memory freed as a unit */
+#define ETCHARRAYLIST_CONTENT_OBJECT 1 /* content is etchobject */
+#define ETCHARRAYLIST_SYNCHRONIZED TRUE
+
+//TODO: Check´, why this is needed
+typedef int (*arraycallback) (int, void*); /* arraylist callback signature */
+
+
+/**
+ * etch_arraylist: data structure encapsulating the arraylist
+ * if is_readonly is set, content is read only and therefore is_free_content
+ * will have no effect -- list items may still be inserted and removed. This
+ * guards against accidentally freeing memory during arraylist destruction
+ * for arrays of references which are still intended to remain in use.
+ */
+typedef struct etch_arraylist
+{
+ etch_object object;
+
+ void** base;
+ unsigned short content_obj_type; /* etch obj_type of a native value */
+ unsigned short content_class_id; /* etch class_id of a native value */
+ unsigned int count;
+
+ /* this object may be masked by etch_collection_mask to determine content
+ * type and class, so do not add any fields above this comment */
+
+ unsigned int delta;
+ unsigned int size;
+ i_iterable iterable;
+
+ byte is_readonly;
+ byte content_type;
+
+ arraycallback freehook; /* hook for free list content */
+
+} etch_arraylist;
+
+
+etch_arraylist* new_etch_arraylist(const unsigned int initialsize, const unsigned int deltasize);
+etch_arraylist* new_etch_arraylist_from(etch_arraylist*);
+etch_arraylist* new_etch_arraylist_synchronized(const unsigned int initialsize, const unsigned int deltasize);
+etch_arraylist* new_etch_arraylist_synchronized_from(etch_arraylist*);
+
+int etch_arraylist_add (etch_arraylist*, void* content);
+int etch_arraylist_add_from (etch_arraylist*, etch_arraylist* newitems, etch_comparator);
+int etch_arraylist_containsp(etch_arraylist*, void* content, const unsigned startat); /* pointer compare */
+int etch_arraylist_contains (etch_arraylist*, void* content, const unsigned startat, etch_comparator);
+int etch_arraylist_count(etch_arraylist* list);
+int etch_arraylist_indexofp (etch_arraylist*, void* content, const unsigned startat); /* pointer compare */
+int etch_arraylist_indexof (etch_arraylist*, void* content, const unsigned startat, etch_comparator);
+int etch_arraylist_remove_content(etch_arraylist*, void* content, const unsigned startat, etch_comparator);
+int etch_arraylist_insert (etch_arraylist*, const unsigned i, void* content);
+int etch_arraylist_remove (etch_arraylist*, const unsigned i, const int is_free_content);
+int etch_arraylist_set (etch_arraylist*, const unsigned i, void* content);
+void* etch_arraylist_get (etch_arraylist*, const unsigned i);
+void etch_arraylist_clear (etch_arraylist*, const int is_free_content);
+void etch_arraylist_destroy (etch_arraylist*, const int is_free_content);
+void etch_arraylist_copyattrs(etch_arraylist* to, etch_arraylist* from);
+
+/*
+ * explicit synchronization locking methods.
+ * these should be used only for locking a list during list iteration.
+ * for synchronization of list operations, the presence of list.synchook
+ * and list.synclock is sufficient.
+ */
+int etch_arraylist_getlock (etch_arraylist*);
+int etch_arraylist_trylock (etch_arraylist*);
+int etch_arraylist_rellock (etch_arraylist*);
+
+/* i_iterable function overrides */
+int etch_arraylist_iterable_first (etch_iterator*);
+int etch_arraylist_iterable_next (etch_iterator*);
+int etch_arraylist_iterable_has_next(etch_iterator*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHARRAYLIST_H */
diff --git a/binding-c/runtime/c/include/etch_arrayval.h b/binding-c/runtime/c/include/etch_arrayval.h
new file mode 100644
index 0000000..4cfbdc4
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_arrayval.h
@@ -0,0 +1,100 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_arrayval.h
+ */
+
+#ifndef ETCHARRAYVAL_H
+#define ETCHARRAYVAL_H
+
+#include "etch_msgutl.h"
+#include "etch_arraylist.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define ETCHARRAYVAL_READONLY TRUE
+#define ETCHARRAYVAL_NOTREADONLY FALSE
+
+/**
+ * etch_arrayvalue
+ * an array of values, where each value is a pointer to an etch object.
+ */
+typedef struct etch_arrayvalue
+{
+ etch_object object;
+
+ etch_arraylist* list;
+ unsigned short content_obj_type; /* etch obj_type of a native value */
+ unsigned short content_class_id; /* etch class_id of a native value */
+ int dim;
+
+ /* this object may be masked by etch_collection_mask to determine content
+ * type and class, so do not add any fields above this comment */
+
+ unsigned short content_item_size; /* byte count of a native value */
+ unsigned char is_array_owned; /* arrayvalue owns natarray? */
+ signed char type_code; /* external content type */
+
+ etch_type* custom_struct_type; /* not owned */
+ struct etch_nativearray* natarray; /* see is_array_owned */
+} etch_arrayvalue;
+
+/**
+ * new_arrayvalue_arraylist()
+ * allocates and returns an arraylist configured appropriately for use as arrayvalue backing store
+ */
+etch_arraylist* new_arrayvalue_arraylist(const int initsize, const int deltsize,
+ const int is_readonly, const int is_synchronized);
+
+/* public methods on etch_arrayvalue */
+etch_arrayvalue* new_arrayvalue(const byte type_code, etch_type* custom_struct_type,
+ const int dim, const int initsize, const int deltsize,
+ const int is_readonly, const int is_synchronized);
+
+etch_arrayvalue* new_arrayvalue_from(struct etch_nativearray* natarray,
+ const signed char type_code, etch_type* custom_struct_type,
+ const int initsize, const int deltsize,
+ const int is_readonly);
+
+etch_arrayvalue* new_arrayvalue_default();
+
+ETCH_ARRAY_ELEMENT* new_array_element(const int objtype);
+
+int arrayvalue_add (etch_arrayvalue* thisp, ETCH_ARRAY_ELEMENT* content);
+
+int arrayvalue_count(etch_arrayvalue*);
+void* arrayvalue_get(etch_arrayvalue*, const int index);
+
+int arrayvalue_set_iterator(etch_arrayvalue*, etch_iterator*);
+void arrayvalue_set_static_content(etch_arrayvalue*, const int is_set);
+
+int arrayvalue_to_nativearray(etch_arrayvalue*);
+
+signed char arrayvalue_get_external_typecode (unsigned short, unsigned short);
+signed char etch_classid_to_arraytype (const unsigned short class_id);
+unsigned short etch_arraytype_to_classid (const signed char typecode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHARRAYVAL_H */
diff --git a/binding-c/runtime/c/include/etch_binary_tdi.h b/binding-c/runtime/c/include/etch_binary_tdi.h
new file mode 100644
index 0000000..09c30cc
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_binary_tdi.h
@@ -0,0 +1,138 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_binary_tdi.h
+ * binary tagged data input
+ */
+
+#ifndef ETCH_BINARY_TDI_H
+#define ETCH_BINARY_TDI_H
+
+#include "etch_tagged_data.h"
+#include "etch_tagdata_inp.h"
+#include "etch_flexbuffer.h"
+#include "etch_value_factory.h"
+
+#if(0)
+
+ BINARY_TAGGED_DATA_INPUT
+ value_factory vf;
+ ctor(valuefactory);
+
+ BINARY_TAGGED_DATA
+ bool (*check_value)(object value, validator v);
+ byte (*adjust_small_value)(byte type, object value);
+ byte (*check_value)(object value);
+ byte (*get_native_type_code)(class*);
+ byte (*get_custom_struct_type)(class*);
+ class*(*get_native_type)(byte type);
+ byte (*check_byte) (byte v, byte is1, byte dt);
+ byte (*check_short) (short v, byte is2, byte dt);
+ byte (*check_int) (int v, byte is4, byte dt);
+ byte (*check_long) (long v, byte is8, byte dt);
+
+ TAGGED_DATA
+ array_value* (*to_array_value) (object value);
+ object* (*from_array_value) (object value);
+ object* (*alloc_array_value)(byte type, Type custtructtype, int dim, int len);
+ class* (*get_component_type)(byte type, Type custtructtype, int dim);
+ byte (*check_value) (object value);
+ byte (*get_native_typecode) (class* c);
+ byte (*get_custom_struct_type)(class*);
+ class* (*get_native_type)(byte type);
+
+ TAGGED_DATA_INPUT
+ etch_message* (*start_message) (tagged_data_input*);
+ etch_message* (*end_message) (tagged_data_input*, etch_message*);
+ etch_structvalue* (*start_struct) (tagged_data_input*);
+ int (*read_struct_element) (tagged_data_input*, struct etch_struct_element*);
+ int (*end_struct) (tagged_data_input*, etch_structvalue*);
+ etch_arrayvalue* (*start_array) (tagged_data_input*);
+ int (*read_array_element) (tagged_data_input*, ETCH_ARRAY_ELEMENT**);
+ int (*end_array) (tagged_data_input*, etch_arrayvalue*);
+
+#endif
+
+
+/**
+ * i_binary_tdi: vtable for binary tagged data input interface
+ */
+struct i_binary_tdi
+{
+ etch_object object;
+
+ etchparentinfo* inherits_from;
+
+ /* i_tagdata_input */
+ etchtdi_start_message start_message;
+ etchtdi_read_message read_message;
+ etchtdi_end_message end_message;
+ etchtdi_start_struct start_struct;
+ etchtdi_read_struct read_struct;
+ etchtdi_end_struct end_struct;
+ etchtdi_start_array start_array;
+ etchtdi_read_array read_array;
+ etchtdi_end_array end_array;
+
+ tagdata_get_native_type_code get_native_type_code;
+ tagdata_get_native_type get_native_type;
+ tagdata_get_custom_structtype get_custom_structtype;
+ tagdata_check_value check_value;
+};
+
+typedef struct i_binary_tdi i_binary_tdi;
+
+
+/**
+ * binary_tagged_data_input
+ * binary tagged data input implementation
+ */
+typedef struct binary_tagged_data_input
+{
+ etch_object object;
+
+ etch_object* impl; /* must appear first to mask tagged_data_input */
+
+ etch_value_factory* vf; /* not owned */
+ etch_flexbuffer* flexbuf; /* not owned */
+ etch_validator* vtor_int; /* not owned */
+ etch_object* static_nullobj; /* owned */
+ etch_object* static_eodmarker; /* owned */
+ etch_string* static_emptystring; /* owned */
+
+} binary_tagged_data_input;
+
+
+ binary_tagged_data_input* new_binary_tagdata_input(etch_value_factory*);
+ tagged_data_input* new_binary_tdi(etch_value_factory*);
+
+struct etch_message* bintdi_read_message(tagged_data_input* tdix,
+ etch_flexbuffer* fbuf);
+
+struct etch_message* bintdi_read_message_fromf(etch_value_factory* vf,
+ etch_flexbuffer* fbuf);
+
+struct etch_message* bintdi_read_message_from(etch_value_factory* vf, byte* buf,
+ const int bufsize);
+
+struct etch_message* bintdi_read_message_fromo(etch_value_factory* vf, byte* buf,
+ const int bufsize, const int msglen, const int offset);
+
+
+#endif /* #ifndef ETCH_BINARY_TDI_H */
diff --git a/binding-c/runtime/c/include/etch_binary_tdo.h b/binding-c/runtime/c/include/etch_binary_tdo.h
new file mode 100644
index 0000000..4425b88
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_binary_tdo.h
@@ -0,0 +1,98 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_binary_tdo.h
+ * binary tagged data output
+ */
+
+#ifndef ETCH_BINARY_TDO_H
+#define ETCH_BINARY_TDO_H
+
+#include "etch_tagdata_out.h"
+#include "etch_flexbuffer.h"
+#include "etch_message.h"
+#include "etch_value_factory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct etch_arrayvalue;
+
+/**
+ * i_binary_tdo: vtable for binary tagged data output interface
+ */
+typedef struct i_binary_tdo
+{
+ etch_object object;
+
+ etchparentinfo* inherits_from;
+
+ /* i_tagdata_output */
+ etchtdo_start_message start_message;
+ etchtdo_write_message write_message;
+ etchtdo_end_message end_message;
+ etchtdo_start_struct start_struct;
+ etchtdo_write_struct write_struct;
+ etchtdo_end_struct end_struct;
+ etchtdo_start_array start_array;
+ etchtdo_write_array write_array;
+ etchtdo_end_array end_array;
+
+ tagdata_get_native_type_code get_native_type_code;
+ tagdata_get_native_type get_native_type;
+ tagdata_get_custom_structtype get_custom_structtype;
+ tagdata_check_value check_value;
+} i_binary_tdo;
+
+/**
+ * binary_tagged_data_output
+ * binary tagged data input implementation
+ */
+typedef struct binary_tagged_data_output
+{
+ etch_object object;
+
+ etch_object* impl; /* must appear first to mask tagged_data_output */
+
+ etch_value_factory* vf; /* not owned */
+ etch_flexbuffer* flexbuf; /* usually owned */
+ unsigned char is_flexbuf_owned;
+
+ etch_validator* vtor_int; /* not owned */
+ etch_validator* vtor_eod; /* not owned */
+ etch_object* static_nullobj; /* owned */
+ etch_object* static_eodmarker; /* owned */
+ etch_string* static_emptystring; /* owned */
+
+} binary_tagged_data_output;
+
+binary_tagged_data_output* new_binary_tagdata_output(etch_value_factory*, etch_flexbuffer*);
+tagged_data_output* new_binary_tdo(etch_value_factory*);
+
+int bintdo_get_bytes (etch_message*, etch_value_factory*, byte** out);
+int bintdo_write_value (binary_tagged_data_output*, etch_validator*, etch_object*);
+struct etch_arrayvalue* bintdo_to_arrayvalue (binary_tagged_data_output* tdo, struct etch_nativearray*);
+struct etch_arrayvalue* normalize_etch_array(void* a, const int maxdim);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* #ifndef ETCH_BINARY_TDO_H */
diff --git a/binding-c/runtime/c/include/etch_cache.h b/binding-c/runtime/c/include/etch_cache.h
new file mode 100644
index 0000000..c477b21
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_cache.h
@@ -0,0 +1,91 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_cache.h -- etch caching
+ */
+
+#ifndef _ETCH_CACHE_H_
+#define _ETCH_CACHE_H_
+
+#include "etch.h"
+#include "etch_runtime.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*etch_cache_freehook_func)(void*, void*);
+
+// etch_cache type
+typedef struct etch_hashtable etch_cache_t;
+
+/**
+ * create a new cache instance.
+ */
+etch_status_t etch_cache_create();
+
+/**
+ * set freehook for cache values.
+ */
+etch_status_t etch_cache_set_freehook(etch_cache_freehook_func freehook);
+
+/**
+ * locate cached object with specified key, returning it or NULL
+ * @param typ specified key
+ * @param out TODO: doc
+ * @return TODO: doc
+ */
+void* etch_cache_find(const unsigned int typ, void** out);
+
+/**
+ * Remove specified object from cache given integer key.
+ * @param typ specified key
+ * @return pointer to cached object, which is not freed here.
+ */
+void* etch_cache_del(const unsigned int typ);
+
+/**
+ * Add specified object to cache given integer key.
+ * @param typ specified key
+ * @return return 0 or -1.
+ */
+int etch_cache_add(const unsigned int typ, void* data);
+
+/**
+ * Add specified object to cache with existence test optional.
+ * @param typ TODO: doc
+ * @param data TODO: doc
+ * @param is_check TODO: doc
+ * @return inserted item hash, or zero.
+ */
+int etch_cache_insert(const unsigned int typ, void* data, const int is_check);
+
+
+/**
+ * clear the cache and destroy the cache object
+ * @return TODO: doc
+ */
+int etch_cache_destroy();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ETCH_CACHE_H_ */
diff --git a/binding-c/runtime/c/include/etch_collection.h b/binding-c/runtime/c/include/etch_collection.h
new file mode 100644
index 0000000..dd3a252
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_collection.h
@@ -0,0 +1,122 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_collection.h
+ * definitions common to collections
+ */
+
+#ifndef ETCHCOLLECTION_H
+#define ETCHCOLLECTION_H
+
+#include "etch.h"
+#include "etch_object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_SYNC_SET 1
+#define ETCH_SYNC_TRY 2
+#define ETCH_SYNC_REL 3
+#define ETCH_SYNC_DEL 4
+
+#define ETCH_SYNC_SET_ ((void*) ETCH_SYNC_SET)
+#define ETCH_SYNC_TRY_ ((void*) ETCH_SYNC_TRY)
+#define ETCH_SYNC_REL_ ((void*) ETCH_SYNC_REL)
+#define ETCH_SYNC_DEL_ ((void*) ETCH_SYNC_DEL)
+
+struct i_iterable;
+struct etch_iterator;
+
+/**
+ * method signatures for i_iterable interface.
+ * each accepts an iterator object as first parameter. the iterator constructor
+ * should call first(), thus establishing a current position. next() should operate
+ * as first() if there is no current position.
+ */
+typedef int (*iterable_first) (struct etch_iterator*);
+typedef int (*iterable_next) (struct etch_iterator*);
+typedef int (*iterable_has_next) (struct etch_iterator*);
+
+/**
+ * etch_iterator: iterator object for any i_iterable.
+ */
+typedef struct etch_iterator
+{
+ etch_object object;
+
+ iterable_first first;
+ iterable_next next;
+ iterable_has_next has_next;
+
+ int ordinal; /* 1-based position index */
+ void* collection; /* underlying collection */
+ void* current_value; /* data at current posn */
+ void* current_key; /* key at current posn */
+ unsigned char is_own_collection;
+} etch_iterator;
+
+
+
+/**
+ * i_iterable: vtable for iterable interface
+ */
+struct i_iterable
+{
+ etch_object object;
+
+ etchparentinfo* inherits_from;
+
+ iterable_first first; /* set current position at first entry */
+ iterable_next next; /* set current position at next entry */
+ iterable_has_next has_next; /* return TRUE if next will succeed */
+};
+
+typedef struct i_iterable i_iterable;
+
+
+/**
+ * new_iterable
+ * constructor for i_iterable interface
+ */
+i_iterable* new_iterable(i_iterable* thisp, i_iterable* parent,
+ iterable_first, iterable_next, iterable_has_next);
+
+/**
+ * new_iterator(), set_iterator(), destroy_iterator
+ * constructors and destructors for etch_iterator
+ */
+etch_iterator* new_iterator(void* collection, i_iterable* iterable);
+
+int set_iterator(etch_iterator* thisp, void* collection, i_iterable* iterable);
+
+/* should be static! call via vtable */
+int destroy_iterator(void*);
+
+etch_iterator* new_empty_iterator();
+
+
+int etch_comparator_noteq(void* a, void* b);
+int etch_comparator_equal(void* a, void* b);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHCOLLECTION_H */
diff --git a/binding-c/runtime/c/include/etch_config.h b/binding-c/runtime/c/include/etch_config.h
new file mode 100644
index 0000000..dd13855
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_config.h
@@ -0,0 +1,124 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_config.h -- configuration items and file
+ */
+
+#ifndef _ETCH_CONFIG_H_
+#define _ETCH_CONFIG_H_
+
+#include "etch_errno.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct etch_config_t etch_config_t;
+
+/**
+ * create a new configuration.
+ * default config properties:
+ * TODO: document default properties
+ * @param config that will be created.
+ */
+etch_status_t etch_config_create(etch_config_t** config);
+
+/**
+ * reads the given config file.
+ * @config handle.
+ * @param config configuration handle.
+ * @param filepath to the file which holds the configuration.
+ */
+etch_status_t etch_config_open(etch_config_t* config, const char* filepath);
+
+/**
+ * sets a new property value
+ * @config handle.
+ * @name of the property.
+ * @value of the property.
+ */
+etch_status_t etch_config_set_property(etch_config_t* config, const char* name, const char* value);
+
+/**
+ * check if property with name existis.
+ * @config handle.
+ * @param name of the property.
+ * @return true if exists, else false.
+ */
+int etch_config_has_property(etch_config_t* config, const char* name);
+
+/**
+ * get the string property by name.
+ * @config handle.
+ * @name of the property.
+ * @value char** where the value will be written
+ * @return etch_status ETCH_SUCCESS or ETCH_EINVAL
+ */
+etch_status_t etch_config_get_property_string(etch_config_t* config, const char* name, char** value);
+
+/**
+ * get the int32 property by name.
+ * @config handle.
+ * @name of the property.
+ * @value where the value will be written
+ * @return etch_status ETCH_SUCCESS or ETCH_EINVAL
+ */
+etch_status_t etch_config_get_property_int(etch_config_t* config, const char* name, int32* value);
+
+/**
+ * gets the property by index.
+ * @config handle.
+ * @name of the property.
+ * @value char** where the value will be written; the value has to be freed with etch_free after use.
+ * @return etch_status ETCH_SUCCESS or ETCH_EINVAL
+ */
+etch_status_t etch_config_get_property_by_index(etch_config_t* config, uint16 index, char** value);
+
+/**
+ * clear the property list.
+ * @config handle.
+ * @return etch_status_t
+ */
+etch_status_t etch_config_clear(etch_config_t* config);
+
+/**
+ * gets the length of the property list.
+ * @config handle.
+ * @return length
+ */
+uint16 etch_config_get_length(etch_config_t* config);
+
+/**
+ * gets the size of the property list.
+ * @config handle.
+ * @return length
+ */
+uint16 etch_config_get_size(etch_config_t* config);
+
+/**
+ * destroy the configure instance.
+ * @config handle.
+ */
+etch_status_t etch_config_destroy(etch_config_t* config);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ETCH_CONFIG_H_ */
diff --git a/binding-c/runtime/c/include/etch_connection.h b/binding-c/runtime/c/include/etch_connection.h
new file mode 100644
index 0000000..4e1a9ff
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_connection.h
@@ -0,0 +1,172 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_connection.h
+ * connection client and server classes - tcp, udp
+ */
+
+#ifndef ETCHCONNECTION_H
+#define ETCHCONNECTION_H
+
+#include "apr_thread_proc.h"
+#include <apr_network_io.h>
+#include <apr_thread_mutex.h>
+#include <apr_thread_cond.h>
+#include "etch_transport_data.h"
+#include "etch_transportint.h"
+#include "etch_sessionint.h"
+#include "etch_thread.h"
+#include "etch_mutex.h"
+#include "etch_wait.h"
+#include "etch_url.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef apr_socket_t etch_rawsocket;
+
+typedef int (*etch_set_socket_options) (void* conxn);
+typedef int (*etch_connection_event_handler) (void* conxn, const int, int, void*);
+
+int etch_defconx_on_event(void* conxn, const int, int, void*);
+int etch_defconx_on_data (void* conxn, const int, int, void*);
+
+#define IS_ETCH_SOCKET_TIMEOUT(n) (APR_TIMEUP == n || 730060 == n)
+#define ETCH_CONX_DEFAULT_BUFSIZE (1024 * 32)
+#define ETCH_CONX_NO_LINGER FALSE
+#define ETCH_CONX_USE_LINGER TRUE
+#define ETCH_CONX_NOT_RECONNECTING FALSE
+#define ETCH_CONX_IS_RECONNECTING TRUE
+#define ETCH_THIS_END_CLOSED (-2)
+#define ETCH_OTHER_END_CLOSED (-3)
+#define ETCH_SHUTDOWN_NOTIFIED (-4)
+#define APR_THIS_END_CLOSED 730053
+#define APR_OTHER_END_CLOSED 730054
+
+
+/*
+ * etch_connection
+ * abstract base class, never instantiated so not an object
+ */
+typedef struct etch_connection
+{
+ unsigned sig;
+ unsigned conxid;
+ void* owner;
+ apr_socket_t* socket;
+ apr_sockaddr_t* sockdata;
+ apr_pool_t* aprpool;
+ etch_mutex* mutex;
+ unsigned bufsize;
+ etch_object* listener;
+ etch_thread* thread;
+ unsigned char is_ownpool;
+ unsigned char is_started;
+ unsigned char is_closing;
+ unsigned char unused;
+
+ etch_wait_t* wait_up; /* wait object - owned */
+ etch_wait_t* wait_down; /* wait object - owned */
+ etch_object* session; /* session data - not owned */
+ char* hostname; /* ansi string - owned */
+ int64 waitcond; /* up/down wait condition */
+ int port;
+ int delay;
+
+ etch_connection_event_handler on_data;
+ etch_connection_event_handler on_event;
+ etch_set_socket_options set_socket_options;
+
+} etch_connection;
+
+
+
+#define ETCH_DEFAULT_SOCKET_FAMILY APR_INET
+extern unsigned connection_id_farm;
+
+#define ETCH_CONNECTION_DEFLINGERTIME 30
+#define ETCH_CONNECTION_DEFNODELAY TRUE
+#define ETCH_CONNECTION_DEFRETRYATTEMPTS 0
+#define ETCH_CONNECTION_DEFRETRYDELAYMS 1000
+
+extern const wchar_t* ETCH_CONNECTION_RECONDELAY;
+extern const wchar_t* ETCH_CONNECTION_AUTOFLUSH;
+extern const wchar_t* ETCH_CONNECTION_KEEPALIVE;
+extern const wchar_t* ETCH_CONNECTION_LINGERTIME;
+extern const wchar_t* ETCH_CONNECTION_NODELAY;
+extern const wchar_t* ETCH_CONNECTION_TRAFCLASS;
+extern const wchar_t* ETCH_CONNECTION_BUFSIZE;
+extern const wchar_t* ETCH_TCPLISTENER_BACKLOG;
+
+#define ETCH_CONXEVT_CREATED 0x1
+#define ETCH_CONXEVT_CREATERR 0x2
+#define ETCH_CONXEVT_SHUTDOWN 0x3
+#define ETCH_CONXEVT_DATA 0x4
+#define ETCH_CONXEVT_RECEIVING 0x5
+#define ETCH_CONXEVT_RECEIVETIMEOUT 0x6
+#define ETCH_CONXEVT_RECEIVERR 0x7
+#define ETCH_CONXEVT_RECEIVED 0x8
+#define ETCH_CONXEVT_RECEIVEND 0x9
+#define ETCH_CONXEVT_SENDING 0xa
+#define ETCH_CONXEVT_SENDTIMEOUT 0xb
+#define ETCH_CONXEVT_SENDERR 0xc
+#define ETCH_CONXEVT_SENT 0xd
+#define ETCH_CONXEVT_SENDEND 0xe
+#define ETCH_CONXEVT_CONXCLOSED 0xf
+#define ETCH_CONXEVT_PEERCLOSED 0x10
+#define ETCH_CONXEVT_OPENING 0x11
+#define ETCH_CONXEVT_OPENERR 0x12
+#define ETCH_CONXEVT_OPENED 0x13
+#define ETCH_CONXEVT_STARTING 0x14
+#define ETCH_CONXEVT_STARTERR 0x15
+#define ETCH_CONXEVT_STARTED 0x16
+#define ETCH_CONXEVT_ACCEPTPUMPEXIT 0x1e
+#define ETCH_CONXEVT_ACCEPTPUMPEXITERR 0x1f
+#define ETCH_CONXEVT_RCVPUMP_START 0x20
+#define ETCH_CONXEVT_RCVPUMP_RECEIVING 0x21
+#define ETCH_CONXEVT_RCVPUMP_ERR 0x22
+#define ETCH_CONXEVT_RCVPUMP_STOP 0x23
+#define ETCH_CONXEVT_ACCEPTING 0x25
+#define ETCH_CONXEVT_ACCEPTERR 0x26
+#define ETCH_CONXEVT_ACCEPTED 0x27
+#define ETCH_CONXEVT_LISTENED 0x28
+#define ETCH_CONXEVT_UP 0x2a
+#define ETCH_CONXEVT_DOWN 0x2b
+#define ETCH_CONXEVT_SOCKOPTERR 0x2f
+#define ETCH_CONXEVT_STOPPING 0x30
+#define ETCH_CONXEVT_STOPERR 0x31
+#define ETCH_CONXEVT_STOPPED 0x32
+#define ETCH_CONXEVT_CLOSING 0x33
+#define ETCH_CONXEVT_CLOSERR 0x34
+#define ETCH_CONXEVT_CLOSED 0x35
+#define ETCH_CONXEVT_DESTRUCTOR 0x3a
+#define ETCH_CONXEVT_DESTROYING 0x3b
+#define ETCH_CONXEVT_DESTROYED 0x3c
+
+int etch_init_connection(etch_connection*, etch_rawsocket*, void* owner);
+int etch_destroy_connection(etch_connection*);
+int etchconx_wait_up (etch_connection*, int timeoutms);
+int etchconx_wait_down(etch_connection*, int timeoutms);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHCONNECTION_H */
diff --git a/binding-c/runtime/c/include/etch_default_value_factory.h b/binding-c/runtime/c/include/etch_default_value_factory.h
new file mode 100644
index 0000000..aa5cc9a
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_default_value_factory.h
@@ -0,0 +1,142 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_defvalufact.h
+ * default implementation of value factory
+ */
+
+#ifndef ETCH_DEFVALUFACT_H
+#define ETCH_DEFVALUFACT_H
+
+#include "etch_message.h"
+#include "etch_exception.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_DEFVF_IDNMAP_DEFINITSIZE 64
+#define ETCH_DEVVF_C2TMAP_DEFINITSIZE 16
+#define ETCH_DEVVF_MIXINS_DEFINITSIZE 4
+
+typedef etch_hashtable vf_idname_map;
+typedef etch_hashtable class_to_type_map;
+
+
+/**
+ * default_value_factory
+ * value factory base implementation.
+ */
+typedef struct default_value_factory
+{
+ etch_object object;
+
+ etch_object* impl;
+
+ class_to_type_map* class_to_type;
+ vf_idname_map* types;
+ etch_arraylist* mixins;
+
+ unsigned char is_own_types;
+ unsigned char is_own_class_to_type;
+
+} default_value_factory;
+
+
+default_value_factory* new_default_value_factory(vf_idname_map* typemap, class_to_type_map* c2tmap);
+default_value_factory* new_default_value_factory_ex(const int objsize, vf_idname_map* typemap, class_to_type_map* c2tmap);
+
+/**
+ * defvf_statics
+ */
+typedef struct defvf_statics
+{
+ etch_type* _mt__etch_runtime_exception;
+ etch_type* _mt__etch_auth_exception;
+ etch_type* _mt__exception;
+ etch_type* _mt__etch_list;
+ etch_type* _mt__etch_map;
+ etch_type* _mt__etch_set;
+ etch_type* _mt__etch_datetime;
+
+ etch_field* _mf_msg;
+ etch_field* _mf__message_id;
+ etch_field* _mf__in_reply_to;
+ etch_field* _mf_result;
+
+} defvf_statics;
+
+
+/*
+ * built-in (etch-global, quasi-static) objects.
+ * these singleton objects are global to all vfs, instantiated with the
+ * initial vf, and destroyed at etch teardown.
+ */
+extern defvf_statics builtins;
+extern unsigned char is_builtins_instantiated;
+
+extern const wchar_t* str_etch_runtime_exception;
+extern const wchar_t* str_etch_auth_exception;
+extern const wchar_t* str_exception;
+extern const wchar_t* str_etch_list;
+extern const wchar_t* str_etch_map;
+extern const wchar_t* str_etch_set;
+extern const wchar_t* str_etch_datetime;
+
+extern const wchar_t* str_msg;
+extern const wchar_t* str_message_id;
+extern const wchar_t* str_in_reply_to;
+extern const wchar_t* str_result;
+
+extern const wchar_t* str_utf8;
+extern const wchar_t* str_keys;
+extern const wchar_t* str_values;
+extern const wchar_t* str_date_time;
+extern const wchar_t* str_keys_values;
+
+extern const wchar_t* str_msgizervf;
+extern const wchar_t* str_msgizerfmt;
+
+/*
+ * etchvf_free_builtins()
+ * frees memory for etch-global quasi-static builtin objects,
+ * and for the validators cache and its validator content.
+ * it should be invoked at etch teardown, after last vf is destroyed.
+ * unit tests will show memory leaks unless they invoke this after test.
+ */
+void etchvf_free_builtins();
+
+int destroy_default_value_factory(void* data);
+int defvf_initialize_static(vf_idname_map* typemap, class_to_type_map* c2tmap);
+etch_type* etchtypemap_get_by_name(etch_hashtable*, const wchar_t* name);
+int defvf_add_mixin(default_value_factory*, etch_value_factory* mixedin_vf);
+int get_etch_string_encoding(etch_value_factory*);
+int etchtypelist_comparator(void* a, void* b);
+unsigned message_get_id32 (etch_message*);
+class_to_type_map* new_class_to_type_map(const int initialsize);
+etch_set* new_vf_types_collection(const int initialsize);
+vf_idname_map* new_vf_idname_map(const int initialsize);
+int class_to_type_map_put(class_to_type_map*, const unsigned, etch_type*);
+etch_type* class_to_type_map_get(class_to_type_map*, const unsigned);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_DEFVALUFACT_H */
diff --git a/binding-c/runtime/c/include/etch_encoding.h b/binding-c/runtime/c/include/etch_encoding.h
new file mode 100644
index 0000000..2a5eb16
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_encoding.h
@@ -0,0 +1,56 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_encoding.h -- character encoding
+ */
+
+#ifndef _ETCH_ENCODING_H_
+#define _ETCH_ENCODING_H_
+
+#include "etch.h"
+#include "etch_mem.h"
+#include "etch_errno.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+etch_status_t etch_encoding_initialize();
+
+etch_status_t etch_encoding_shutdown();
+
+/**
+ * transcode a bytestream from encoding inEncoding to outEncoding
+ * @param out must be freed by the caller via etch_free
+ */
+int etch_encoding_transcode(char** out, unsigned char outEncoding, const char* in, unsigned char inEncoding, unsigned int inByteCount, int* outByteCount, etch_pool_t* pool);
+
+int etch_encoding_transcode_wchar(char** out, unsigned char outEncoding, const wchar_t* in, etch_pool_t* pool);
+
+int etch_encoding_transcode_to_wchar(wchar_t** out, const void* in, unsigned char inEncoding, unsigned int inByteCount, etch_pool_t* pool);
+
+unsigned char etch_encoding_for_wchar();
+
+unsigned int etch_encoding_get_sizeof_terminator(unsigned char encoding);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_ENCODING_H */
diff --git a/binding-c/runtime/c/include/etch_errno.h b/binding-c/runtime/c/include/etch_errno.h
new file mode 100644
index 0000000..1d6ada5
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_errno.h
@@ -0,0 +1,64 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_errno.h -- error number file
+ */
+
+#include "etch.h"
+
+#ifndef _ETCH_ERRNO_H_
+#define _ETCH_ERRNO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Type for specifying an error or status code.
+ */
+typedef int etch_status_t;
+
+/* no error */
+#define ETCH_SUCCESS 0
+/* error */
+#define ETCH_ERROR 1
+/* invalid argument error */
+#define ETCH_EINVAL 2
+/* function not implemented */
+#define ETCH_ENOTIMPL 3
+/* not memory */
+#define ETCH_ENOMEM 4
+/* invalid state */
+#define ETCH_EINVALSTATE 5
+/* invalid state */
+#define ETCH_EFILENOTFOUND 6
+/* busy state */
+#define ETCH_EBUSY 7
+/* timeout */
+#define ETCH_ETIMEOUT 8
+/* index out of bound */
+#define ETCH_EOUTOFBOUNDS 9
+/* IO Error */
+#define ETCH_EIO 10
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* _ETCH_GENERAL_H_ */
diff --git a/binding-c/runtime/c/include/etch_exception.h b/binding-c/runtime/c/include/etch_exception.h
new file mode 100644
index 0000000..2c99b0a
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_exception.h
@@ -0,0 +1,72 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_exception.h -- etch exception
+ */
+
+#ifndef ETCHEXCP_H
+#define ETCHEXCP_H
+
+#include "etch_object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * create a new etch exception
+ */
+etch_exception* new_etch_exception(const excptype_t type);
+
+
+/**
+ * create a new builtin etch exception with errorcode
+ */
+etch_exception* new_etch_exception_from_errorcode(int errorcode);
+
+/**
+ * set message of the exception
+ */
+void etch_exception_set_message(etch_exception* ex, etch_string* mess);
+
+/**
+ * get message of the exception
+ */
+etch_string* etch_exception_get_message(etch_exception* ex);
+
+/**
+ * get exception type
+ */
+etch_status_t etch_exception_get_type(etch_exception* exception, excptype_t* type);
+
+/**
+ * error code of this exception
+ */
+uint32 etch_exception_get_errorcode(etch_exception* ex);
+
+/**
+ * destroy exception
+ */
+int destroy_etch_exception(void* excp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHEXCP_H */
diff --git a/binding-c/runtime/c/include/etch_field.h b/binding-c/runtime/c/include/etch_field.h
new file mode 100644
index 0000000..5d0c5e5
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_field.h
@@ -0,0 +1,56 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_field.h -- defines the etch_field object.
+ * An etch_field is an etch_id_name representing a field of a struct or message
+ * (i.e. a key for a value).
+ */
+
+#ifndef ETCHFIELD_H
+#define ETCHFIELD_H
+
+#include "etch_id_name.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * etch_field
+ * We're simply typef'ing this for now, but we've left it open to later define
+ * an etch_field struct having an etch_id_name as a member.
+ */
+typedef etch_id_name etch_field;
+
+etch_field* new_field(const wchar_t* name);
+etch_field* new_static_field(const wchar_t* name);
+int destroy_static_field(etch_field*);
+
+#define clone_field clone_id_name
+#define destroy_field destroy_id_name
+#define is_good_field is_good_id_name
+#define is_equal_fields is_equal_id_names
+#define compute_field_id compute_id_name_id
+#define compute_field_id_from_widename compute_id_name_id_from_widename
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHFIELD_H */
diff --git a/binding-c/runtime/c/include/etch_flexbuffer.h b/binding-c/runtime/c/include/etch_flexbuffer.h
new file mode 100644
index 0000000..1e9ccf4
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_flexbuffer.h
@@ -0,0 +1,97 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * a flex_buffer wraps a byte array and manages the active region of
+ * it (0..length). it supports dynamically extending the buffer.
+ *
+ * a flexbuffer has an index (read or write cursor). the various get
+ * and put operations always occur at the current index, with the index
+ * adjusted appropriately afterward. get() will not move the index past
+ * length. if put needs to move index past length, length is also
+ * adjusted. this may cause the byte array to be re-allocated.
+ */
+
+#ifndef ETCH_FLEX_BUFFER_H
+#define ETCH_FLEX_BUFFER_H
+
+#include "etch_object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_FLEXBUF_PUT_ALL (-1)
+
+
+typedef struct etch_flexbuffer
+{
+ etch_object object;
+
+ unsigned char *buf;
+ size_t bufsize; /* buffer size */
+ size_t datalen; /* data length */
+ size_t index; /* current position */
+ unsigned char is_littleendian;
+
+} etch_flexbuffer;
+
+etch_flexbuffer *new_flexbuffer(size_t);
+etch_flexbuffer *new_flexwriter_from (void *buf, size_t, size_t);
+etch_flexbuffer *new_flexbuffer_from (void *buf, size_t, size_t, size_t);
+etch_flexbuffer *etch_flexbuf_create_b (void *buf, size_t, size_t);
+etch_flexbuffer *etch_flexbuf_create_bi(void *buf, size_t, size_t, size_t);
+etch_flexbuffer *etch_flexbuf_skip (etch_flexbuffer*, size_t, int);
+
+byte* etch_flexbuf_get_buffer(etch_flexbuffer*);
+int destroy_etch_flexbuffer(void*);
+int etch_flexbuf_set_length(etch_flexbuffer*, size_t);
+int etch_flexbuf_set_index (etch_flexbuffer*, size_t);
+void etch_flexbuf_clear (etch_flexbuffer*);
+size_t etch_flexbuf_avail (etch_flexbuffer*);
+int etch_flexbuffer_reset_to (etch_flexbuffer*, size_t);
+etch_flexbuffer *etch_flexbuf_compact(etch_flexbuffer*);
+etch_flexbuffer *etch_flexbuf_reset (etch_flexbuffer*);
+
+size_t etch_flexbuf_get (etch_flexbuffer*, byte*, size_t, size_t);
+
+int etch_flexbuf_get_byte (etch_flexbuffer*, byte* out);
+int etch_flexbuf_get_short (etch_flexbuffer*, short* out);
+int etch_flexbuf_get_int (etch_flexbuffer*, int* out);
+int etch_flexbuf_get_long (etch_flexbuffer*, int64* out);
+int etch_flexbuf_get_float (etch_flexbuffer*, float* out);
+int etch_flexbuf_get_double(etch_flexbuffer*, double* out);
+
+size_t etch_flexbuf_get_fully (etch_flexbuffer*, byte*, size_t);
+byte* etch_flexbuf_get_all (etch_flexbuffer*, size_t* out_count);
+byte* etch_flexbuf_get_allfrom(etch_flexbuffer*, size_t, size_t* out_count);
+
+size_t etch_flexbuf_put (etch_flexbuffer*, byte*, size_t, size_t);
+size_t etch_flexbuf_put_from (etch_flexbuffer*, etch_flexbuffer*, size_t);
+size_t etch_flexbuf_put_byte (etch_flexbuffer*, byte value);
+size_t etch_flexbuf_put_short (etch_flexbuffer*, short value);
+size_t etch_flexbuf_put_int (etch_flexbuffer*, int value);
+size_t etch_flexbuf_put_long (etch_flexbuffer*, int64 value);
+size_t etch_flexbuf_put_float (etch_flexbuffer*, float value);
+size_t etch_flexbuf_put_double (etch_flexbuffer*, double value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCH_FLEX_BUFFER_H */
diff --git a/binding-c/runtime/c/include/etch_general.h b/binding-c/runtime/c/include/etch_general.h
new file mode 100644
index 0000000..b457025
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_general.h
@@ -0,0 +1,72 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_general.h -- general file
+ */
+
+#ifndef _ETCH_GENERAL_H_
+#define _ETCH_GENERAL_H_
+
+#include "etch.h"
+#include "etch_mem.h"
+#include "etch_errno.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * get_dynamic_classid()
+ * get a class ID for objects not known to the binding,
+ * class ID sequence for generated classes.
+ */
+unsigned short get_dynamic_classid();
+
+/**
+ * get_dynamic_classid_unique()
+ * if specified ID already assigned, return it; otherwise generate and return.
+ * assignment is currently non-atomic.
+ */
+unsigned short get_dynamic_classid_unique(unsigned short* globalid);
+
+/**
+ * remove spaces and tabs on left and right side
+ * from given string.
+ * @param str to trim
+ * @return trimed string.
+ */
+char* strtrim(char* str);
+
+/**
+ * waitkey()
+ * method to wait for a keypress.
+ */
+void waitkey();
+
+int etch_snwprintf(wchar_t *buffer, size_t count, const wchar_t *format, ...);
+
+//todo: refactor this stuff
+int hexchar_to_int (const unsigned char hexchar);
+int hexwchar_to_int(const wchar_t hexwchar);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* _ETCH_GENERAL_H_ */
diff --git a/binding-c/runtime/c/include/etch_hash.h b/binding-c/runtime/c/include/etch_hash.h
new file mode 100644
index 0000000..10192c6
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_hash.h
@@ -0,0 +1,206 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_hash.h
+ * etch hashtable
+ */
+
+#ifndef ETCHHASH_H
+#define ETCHHASH_H
+
+#include "etch.h"
+#include "etch_mutex.h"
+#include "etch_collection.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_INITIAL_HASHTABLE_SIZE 32768
+#define MIN_INITIAL_HASHTABLE_SIZE 4
+#define ETCH_DEFAULT_HASHTABLE_SIZE 16
+#define ETCHHASHTABLE_CONTENT_OPAQUE 0
+#define ETCHHASHTABLE_CONTENT_OBJECT 1 /* opaque key */
+#define ETCHHASHTABLE_CONTENT_INT_OBJECT 2
+#define ETCHHASHTABLE_CONTENT_LONG_OBJECT 3
+#define ETCHHASHTABLE_CONTENT_STRING_OBJECT 4
+#define ETCHHASHTABLE_CONTENT_OBJECT_OBJECT 5 /* etch_map */
+#define ETCHHASHTABLE_CONTENT_OBJECT_NONE 6 /* etch_set */
+
+#define HASHTABLE_DEFAULT_READONLY_KEYS TRUE
+#define HASHTABLE_DEFAULT_READONLY_VALUES TRUE
+#define HASHTABLE_DEFAULT_TRACKED_MEMORY TRUE
+#define HASHTABLE_DEFAULT_CONTENT_TYPE 0
+
+//TODO: clean up BEGIN
+typedef int (*mapcallback) (void*, void*); /* hashtable callback signature */
+int string_to_object_clear_handler (void*, void*);
+int object_to_object_clear_handler (void*, void*);
+int etch_noop_clear_handler (void* key, void* value);
+//TODO: clean up END
+
+/**
+ * etch_hashitem
+ * an entry in a hashtable.
+ */
+typedef struct etch_hashitem
+{
+ char* key;
+ void* value;
+ unsigned hash;
+
+} etch_hashitem;
+
+/**
+ * the etch C hashtable interface.
+ * all methods of this interface should have implementations (i.e. not be null).
+ */
+typedef struct i_hashtable
+{
+ etch_object object;
+
+ etchparentinfo* inherits_from;
+
+ int (*create) (const int size, void* in, void** out);
+ int (*hdestroy)(void* realtable, void* in, void** out);
+ int (*insert) (void* realtable, void* key, const int keylen, void* data, const int datalen, void* in, void** out);
+ int (*inserth) (void* realtable, void* key, void* data, void* in, void** out);
+ int (*find) (void* realtable, void* key, const int keylen, void* in, void** out);
+ int (*findh) (void* realtable, const unsigned key, void* in, void** out);
+ int (*first) (void* realtable, void* in, void** out);
+ int (*next) (void* realtable, void* in, void** out);
+ int (*current) (void* realtable, void* in, void** out);
+ int (*remove) (void* realtable, void* key, const int keylen, void* in, void** out);
+ int (*removeh) (void* realtable, const unsigned key, void* in, void** out);
+ int (*clear) (void* realtable, const int freekey, const int freeval, void* in, void** out);
+ int (*count) (void* realtable, void* in, void** out);
+ int (*size) (void* realtable, void* in, void** out);
+ int (*stats) (void* realtable, void* in, void** out);
+ int (*hash) (void* realtable, void* key, const int keylen, const int priorhash, void* in, void** out);
+
+} i_etch_hashtable;
+
+/**
+ * etch_hashtable
+ * hashtable object
+ */
+typedef struct etch_hashtable
+{
+ etch_object object;
+
+ void* realtable; /* implementation's hashtable object */
+ unsigned short content_obj_type; /* todo: populate */
+ unsigned short content_class_id; /* todo: populate */
+
+ unsigned char is_readonly_keys;
+ unsigned char is_readonly_values;
+ unsigned char is_tracked_memory;
+ unsigned char content_type;
+
+ /* this object may be masked by etch_collection_mask to determine content
+ * type and class, so do not add any fields above this comment */
+
+ i_iterable iterable; /* iterable interface */
+
+ mapcallback freehook; /* clear() callback if any */
+
+} etch_hashtable;
+
+/**
+ * create a new instance of etch_hashtable.
+ */
+etch_hashtable* new_hashtable(const unsigned int initialsize);
+
+/**
+ * create a new instance of etch_hashtable
+ * that is synchronized.
+ */
+etch_hashtable* new_hashtable_synchronized(const unsigned int initialsize);
+
+/**
+ * create a new instancec of etch_map.
+ */
+etch_hashtable* new_etch_map(const unsigned int initialsize);
+
+/**
+ * create a new instance of etch_set.
+ */
+etch_hashtable* new_etch_set(const unsigned int initialsize);
+
+/**
+ * destory the instance of etch_hashtable.
+ */
+int destroy_hashtable(etch_hashtable* hashtable, const int is_free_k, const int is_free_v);
+
+/*
+ * explicit synchronization locking methods.
+ * these should be used only for locking a map during map iteration.
+ * for synchronization of map operations, the presence of map.synchook
+ * and map.synclock is sufficient.
+ */
+int hashtable_getlock (etch_hashtable*);
+int hashtable_trylock (etch_hashtable*);
+int hashtable_rellock (etch_hashtable*);
+
+//int hashtable_defsynchook(void* action, void* mutex);
+
+/* i_iterable function overrides */
+int hashtable_iterable_first (etch_iterator*);
+int hashtable_iterable_next (etch_iterator*);
+int hashtable_iterable_has_next(etch_iterator*);
+
+//TODO: cleanup
+
+typedef etch_hashtable etch_set;
+etch_set* new_set(const int initialsize);
+
+#define is_etch_set(x) (x && (((etch_object*)x)->obj_type == ETCHTYPEB_HASHTABLE) \
+ && (((etch_hashtable*)x)->content_type == ETCHHASHTABLE_CONTENT_OBJECT_NONE))
+
+int jenkins_insert (void* realtable, void* key, const int keylen, void* data, const int datalen, void* in, void** out);
+int jenkins_inserth(void* realtable, void* key, void* data, void* in, void** out);
+int jenkins_find (void* realtable, void* key, const int keylen, void* in, void** out);
+int jenkins_findh (void* realtable, const unsigned int hashed, void* in, void** out);
+int jenkins_first (void* realtable, void* in, void** out);
+int jenkins_next (void* realtable, void* in, void** out);
+int jenkins_current(void* realtable, void* in, void** out);
+int jenkins_remove (void* realtable, void* key, const int keylen, void* in, void** out);
+int jenkins_clear (void* realtable, const int freekey, const int freeval, void* in, void** out);
+int jenkins_count (void* realtable, void* in, void** out);
+int jenkins_size (void* realtable, void* in, void** out);
+int jenkins_stats (void* realtable, void* in, void** out);
+int jenkins_hash (void* realtable, char* key, const int keylen, const int priorhash, void* in, void** out);
+int jenkins_hashx (char* key, const int keylen, const int priorhash);
+int jenkins_destroy(void* realtable, void* in, void** out);
+int jenkins_create (const int initialsize_items, void* in, void** out);
+etch_hashtable* ctor_jenkins_hashtable(const int initialsize_bits);
+etch_hashtable* new_systemhashtable (const int initialsize_items);
+void delete_systemhashtable(etch_hashtable*);
+
+
+struct etch_hashtable;
+
+struct etch_arraylist* get_map_keys(struct etch_hashtable*);
+struct etch_arraylist* get_map_values(struct etch_hashtable*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHHASH_H */
diff --git a/binding-c/runtime/c/include/etch_id_name.h b/binding-c/runtime/c/include/etch_id_name.h
new file mode 100644
index 0000000..f3a0d71
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_id_name.h
@@ -0,0 +1,68 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_id_name.h
+ * etch_id_name is base class for etch_field and etch_type. it binds
+ * together a type or field name with its associated id. The id is used
+ * for certain operations, such as the key in a map, comparisons, and
+ * binary encoding on the wire.
+*/
+
+#ifndef ETCHIDNAME_H
+#define ETCHIDNAME_H
+
+#include "etch.h"
+#include "etch_object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * etch_id_name
+ */
+struct etch_id_name
+{
+ etch_object object;
+
+ etch_object* impl; /* extension of id_name, if any */
+
+ unsigned id; /* wire ID, not a hashmap key */
+ size_t namebytelen; /* byte length including terminator */
+ wchar_t* name; /* owned */
+ char* aname; /* owned - 8-bit name 1st step in id_name conversion */
+};
+
+typedef struct etch_id_name etch_id_name;
+
+etch_id_name* new_id_name(const wchar_t* name);
+void* clone_id_name(void*);
+int destroy_id_name(void*);
+
+int is_good_id_name(etch_id_name* thisp);
+int is_equal_id_names(etch_id_name*, etch_id_name*);
+int compute_id_name_id(const char*);
+int compute_id_name_id_from_widename(const wchar_t*);
+uint32 id_name_get_hashkey(void*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHIDNAME_H */
diff --git a/binding-c/runtime/c/include/etch_id_name_map.h b/binding-c/runtime/c/include/etch_id_name_map.h
new file mode 100644
index 0000000..3f68422
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_id_name_map.h
@@ -0,0 +1,75 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_id_name.h
+ * etch_id_name is base class for etch_field and etch_type. it binds
+ * together a type or field name with its associated id. The id is used
+ * for certain operations, such as the key in a map, comparisons, and
+ * binary encoding on the wire.
+*/
+
+#ifndef _ETCH_ID_NAME_MAP_H_
+#define _ETCH_ID_NAME_MAP_H_
+
+#include "etch.h"
+#include "etch_object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct etch_id_name_map etch_id_name_map;
+
+/**
+ * creates a new instance of etch_id_name_map
+ */
+etch_status_t etch_id_name_map_create(etch_id_name_map** map);
+
+/**
+ * gets the IdName data which corresponds to the specified id.
+ */
+etch_status_t etch_id_name_map_get_by_id(etch_id_name_map* map, int32 id, void** data);
+
+/**
+ * gets the IdName data which corresponds to the specified id.
+ */
+etch_status_t etch_id_name_map_get_by_name(etch_id_name_map* map, const wchar_t* name, void** data);
+
+/**
+ * adds the id name data to the map.
+ */
+etch_status_t etch_id_name_map_add(etch_id_name_map* map, int32 id, const wchar_t* name, void* data);
+
+/**
+ * get count of the map.
+ */
+uint32 etch_id_name_map_count(etch_id_name_map* map);
+
+// get values function
+
+/**
+ * destroy the etch_id_name_map instance
+ */
+etch_status_t etch_id_name_map_destroy(etch_id_name_map* map);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef _ETCH_ID_NAME_MAP_H_ */
diff --git a/binding-c/runtime/c/include/etch_linked_list.h b/binding-c/runtime/c/include/etch_linked_list.h
new file mode 100644
index 0000000..a7fedd2
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_linked_list.h
@@ -0,0 +1,97 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_linklist.h
+ * linked list implementation.
+ */
+
+#ifndef _ETCH_LINKED_LIST_H_
+#define _ETCH_LINKED_LIST_H_
+
+#include "etch_collection.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_LINKED_LIST_SYNCHRONIZED 1
+#define ETCH_LINKED_LIST_DATA_FREE 2
+
+typedef struct etch_linked_list_t etch_linked_list_t;
+
+/**
+ * create a new linked list instance.
+ */
+etch_status_t etch_linked_list_create(etch_linked_list_t** list, uint8 flags);
+
+/**
+ * add a new element to the list.
+ */
+etch_status_t etch_linked_list_add(etch_linked_list_t* list, void* data);
+
+/**
+ * get element at index.
+ */
+etch_status_t etch_linked_list_get(etch_linked_list_t* list, int32 index, void** data);
+
+/**
+ * get element at index.
+ */
+int32 etch_linked_list_index_of(etch_linked_list_t* list, const void* data);
+
+/**
+ * check the list if the element is inside.
+ */
+uint8 etch_linked_list_contains(etch_linked_list_t* list, const void* data);
+
+/**
+ * insert a new element at the given index to the list.
+ */
+etch_status_t etch_linked_list_insert(etch_linked_list_t* list, int32 index, void* data);
+
+/**
+ * remove an element from the list.
+ */
+etch_status_t etch_linked_list_remove_at(etch_linked_list_t* list, const int32 index);
+
+/**
+ * remove an element from the list.
+ */
+etch_status_t etch_linked_list_remove(etch_linked_list_t* list, void* data);
+
+/**
+ * clear the list.
+ */
+etch_status_t etch_linked_list_clear(etch_linked_list_t* list);
+
+/**
+ * get count of the list.
+ */
+uint32 etch_linked_list_count(etch_linked_list_t* list);
+
+/**
+ * destroy a linked list instance
+ */
+etch_status_t etch_linked_list_destroy(etch_linked_list_t* list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef _ETCH_LINKED_LIST_H_ */
diff --git a/binding-c/runtime/c/include/etch_log.h b/binding-c/runtime/c/include/etch_log.h
new file mode 100644
index 0000000..b568ed9
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_log.h
@@ -0,0 +1,169 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_log.h
+ * logger for the c binding
+ */
+#ifndef _ETCH_LOG_H_
+#define _ETCH_LOG_H_
+
+#include "etch.h"
+#include "etch_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * etch_log macros
+ */
+#define ETCH_LOG(category, level, fmt, ...) etch_log_log(NULL, category, level, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
+#define ETCH_LOGW(category, level, fmt, ...) etch_log_logw(NULL, category, level, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
+
+/**
+ * etch_log_t
+ */
+typedef struct etch_log_t etch_log_t;
+
+/**
+ * etch_log_levels
+ */
+typedef enum etch_log_level
+{
+ /**
+ * extended debug for low-level
+ * or voluminous detail
+ */
+ ETCH_LOG_XDEBUG = 0,
+
+ /**
+ * traces of connection open
+ * close send receive etc
+ */
+ ETCH_LOG_DEBUG,
+
+ /**
+ * display server and client
+ * open and close etc
+ */
+ ETCH_LOG_INFO,
+
+ /**
+ * messages that should be
+ * noted regardless
+ */
+ ETCH_LOG_WARN,
+
+ /**
+ * failures
+ */
+ ETCH_LOG_ERROR
+
+} etch_log_level;
+
+/**
+ * etch_log_message_t
+ */
+typedef struct etch_log_message_t
+{
+ const char* category;
+ time_t timestamp;
+ etch_log_level level;
+ uint32 threadid;
+ const char* file;
+ uint32 line;
+ char* message;
+} etch_log_message_t;
+
+/*
+ * log appender definitions.
+ */
+typedef etch_status_t etch_log_appender_create(void** appender, etch_config_t* config);
+typedef etch_status_t etch_log_appender_open(void* appender);
+typedef etch_status_t etch_log_appender_log(void* appender, etch_log_message_t* message);
+typedef etch_status_t etch_log_appender_close(void* appender);
+typedef etch_status_t etch_log_appender_destroy(void* appender);
+
+typedef struct etch_log_appender_desc {
+ etch_log_appender_create* create;
+ etch_log_appender_open* open;
+ etch_log_appender_log* log;
+ etch_log_appender_close* close;
+ etch_log_appender_destroy* destroy;
+} etch_log_appender_desc;
+
+typedef struct etch_log_appender {
+ struct etch_log_appender_desc* desc;
+ void* data;
+} etch_log_appender;
+
+/**
+ * register a new log appender.
+ * @param name of the appender
+ * @param desc of log appender.
+ * @return status.
+ */
+etch_status_t etch_log_register_appender(const char* name, etch_log_appender_desc* desc);
+
+/**
+ * create a new logger.
+ * @param logger that will be created.
+ * @return status.
+ */
+etch_status_t etch_log_create(etch_log_t** logger, etch_config_t* config);
+
+/**
+ * etch_log_log()
+ * write a log message to console/file/system log depending on configuration.
+ * @param logger logger instance
+ * @param category the category name for this message.
+ * @param level logging level of the message.
+ * @param file filename where the log function is called.
+ * @param line line number where the log function is called.
+ * @param fmt the format string ala printf.
+ * @... a variable length argument list ala printf.
+ * @return void
+ */
+etch_status_t etch_log_log(etch_log_t* logger, const char* category, etch_log_level level, const char* file, int line, const char* fmt, ...);
+
+/**
+ * etch_log_logw()
+ * write a log message to console/file/system log depending on configuration.
+ * @param logger logger instance
+ * @param category the category name for this message.
+ * @param level logging level of the message.
+ * @param file filename where the log function is called.
+ * @param line line number where the log function is called.
+ * @param fmt the format wide string ala wprintf.
+ * @... a variable length argument list ala wprintf.
+ * @return void
+ */
+etch_status_t etch_log_logw(etch_log_t* logger, const char* category, etch_log_level level, const char* file, int line, const wchar_t* fmt, ...);
+
+/**
+ * destroy the logger instance.
+ * @config handle.
+ */
+etch_status_t etch_log_destroy(etch_log_t* logger);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCH_LOG_H */
diff --git a/binding-c/runtime/c/include/etch_mailbox.h b/binding-c/runtime/c/include/etch_mailbox.h
new file mode 100644
index 0000000..0895c20
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_mailbox.h
@@ -0,0 +1,304 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_mailboxint.h
+ * mailbox interface
+ */
+#ifndef ETCHIMAILBOX_H
+#define ETCHIMAILBOX_H
+
+#include "etch_object.h"
+#include "etch_message.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_MAILBOX_TIMEOUT (-2)
+#define ETCH_MAILBOX_DUPLICATE (-3)
+
+struct i_mailbox;
+struct i_mailbox_manager;
+
+/**
+ * etch_mailbox_notify()
+ * notify interface callback to receive notification of mailbox status changes.
+ * @remarks accomodation is made for a single status listener only.
+ * @param thisx caller
+ * @param mb the mailbox whose status has changed.
+ * @param state the state object specified during callback registration.
+ * @param closed true if the mailbox timeout has expired and the mailbox is now closed to delivery,
+ * false if a message has arrived.
+ */
+typedef int (*etch_mailbox_notify) (void* thisx, struct i_mailbox* mb, void* state, const int is_closed);
+
+
+/**
+ * etch_mailbox_element
+ * this is for now an etch object, in case it needs to own the message
+ */
+typedef struct etch_mailbox_element
+{
+ unsigned int hashkey;
+ unsigned short obj_type;
+ unsigned short class_id;
+ struct etch_object* vtab;
+ etch_object_destructor destroy;
+ etch_object_clone clone;
+ obj_gethashkey get_hashkey;
+ struct etch_object* parent;
+ etchresult* result;
+ unsigned int refcount;
+ unsigned int length;
+ unsigned char is_null;
+ unsigned char is_copy;
+ unsigned char is_static;
+ unsigned char reserved;
+ etch_mutex_hookproc synchook;
+ etch_mutex_t* synclock;
+
+ etch_message* msg;
+ etch_who* whofrom;
+
+} etch_mailbox_element;
+
+
+/* - - - - - - - - - - - - - - - - -
+ * signatures of i_mailbox virtuals
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etch_mailbox_message()
+ * queues the specified message to this mailbox.
+ * @param thisx caller
+ * @param msg the message to be received, caller relinquishes on success, retains on failure.
+ * @param whofrom sender, caller retains
+ * @return 0 success, -1 failure.
+ */
+typedef int (*etch_mailbox_message)(void* thisx, etch_who* whofrom, etch_message* msg);
+
+/**
+ * etch_mailbox_read()
+ * reads the next message from the mailbox, waiting indefinitely for a message to be delivered.
+ * @param thisx caller
+ * @param out location to receive the etch_mailbox_element* result.
+ * @return 0 success, -1 failure. on success, out location is populated with the requested message.
+ * if mailbox is empty and closed, result will be zero and *out will contain null.
+ * -1 this indicates an exception condition, not an empty condition.
+ */
+typedef int (*etch_mailbox_read) (void* thisx, etch_mailbox_element** out);
+
+/**
+ * etch_mailbox_read_withwait()
+ * reads the next message from the mailbox, waiting the specified time for a message to be delivered.
+ * @param thisx caller
+ * @param maxdelay the maximum amount of time in milliseconds to wait for a message when the
+ * mailbox is empty. zero indicates wait indefinitely, -1 indicates do not wait.
+ * @param out location to receive the etch_mailbox_element* result.
+ * @return 0 success, -1 failure. on success, out location is populated with the requested message.
+ * if mailbox is empty and closed, result will be zero and *out will contain null.
+ * -1 this indicates an exception condition, not an empty condition.
+ */
+typedef int (*etch_mailbox_read_withwait) (void* thisx, const int maxdelay, etch_mailbox_element** out);
+
+/**
+ * etch_mailbox_close_delivery()
+ * closes the mailbox such that no more messages can be delivered to it.
+ * any messages currently queued will continue to be processed.
+ * @param thisx caller
+ * @return 0 if mailbox was closed successfuly, -1 if mailbox was already closed or could not be closed.
+ */
+typedef int (*etch_mailbox_close_delivery) (void* thisx);
+
+/**
+ * etch_mailbox_close_read()
+ * closes the mailbox such that no more messages can be delivered to it or read from it.
+ * any messages currently queued will be delivered to a default handler.
+ * @param thisx caller
+ * @return 0 if mailbox was closed successfuly, -1 if mailbox was already closed or could not be closed.
+ */
+typedef int (*etch_mailbox_close_read) (void* thisx);
+
+/**
+ * etch_mailbox_register_notify()
+ * register a etch_mailbox_notify callback to receive notification of mailbox status changes.
+ * @param thisx caller
+ * @param notify pointer to a function conforming to the etch_mailbox_notify signature.
+ * @param state a value to pass through via the supplied notify callback.
+ * @param maxdelay the maximum amount of time in milliseconds to wait for delivery of the notification.
+ * zero indicates wait indefinitely, -1 indicates do not wait.
+ * @return 0 on success, -1 if a callback is already registered, or on exception condition.
+ */
+typedef int (*etch_mailbox_register_notify) (void* thisx, etch_mailbox_notify, etch_object* state, const int maxdelay);
+
+/**
+ * etch_mailbox_unregister_notify()
+ * remove the specified etch_mailbox_notify callback. cancels any current timeout.
+ * @param thisx caller
+ * @param notify pointer to a function conforming to the etch_mailbox_notify signature.
+ * @return 0 if specified callback was unregistered or -1 is specified calback is not currently
+ * registered, or on exception condition.
+ */
+typedef int (*etch_mailbox_unregister_notify) (void* thisx, etch_mailbox_notify);
+
+/**
+ * etch_mailbox_is_empty()
+ * @return boolean value indicating if specified mailbox is empty
+ */
+typedef int (*etch_mailbox_is_empty) (void* thisx);
+
+/**
+ * etch_mailbox_is_closed()
+ * @return boolean value indicating if specified mailbox is closed
+ */
+typedef int (*etch_mailbox_is_closed) (void* thisx);
+
+/**
+ * etch_mailbox_is_full()
+ * @return boolean value indicating if specified mailbox is full
+ */
+typedef int (*etch_mailbox_is_full) (void* thisx);
+
+
+/** @return the message id of this mailbox */
+typedef int64 (*etch_mailbox_get_message_id) (void* thiz);
+
+
+/** @return the concrete etch_mailbox object for this mailbox */
+typedef struct etch_plainmailbox etch_mailbox;
+typedef etch_mailbox* (*etch_mailbox_get_implobj) (struct i_mailbox*);
+
+
+/** @return the manager for this mailbox */
+typedef struct i_mailbox_manager* (*etch_mailbox_get_manager) (struct i_mailbox*);
+
+
+
+/* - - - - - - - - - - - - - - - - -
+ * i_mailbox
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * i_mailbox
+ * mailbox interface
+ */
+typedef struct i_mailbox
+{
+ unsigned int hashkey;
+ unsigned short obj_type;
+ unsigned short class_id;
+ struct etch_object* vtab;
+ etch_object_destructor destroy;
+ etch_object_clone clone;
+ obj_gethashkey get_hashkey;
+ struct etch_object* parent;
+ etchresult* result;
+ unsigned int refcount;
+ unsigned int length;
+ unsigned char is_null;
+ unsigned char is_copy;
+ unsigned char is_static;
+ unsigned char reserved;
+ etch_mutex_hookproc synchook;
+ etch_mutex_t* synclock;
+
+ void* thisx; /* instantiating object */
+ etch_mailbox_get_implobj mailbox;
+ etch_mailbox_get_manager manager;
+
+ etch_mailbox_message message;
+ etch_mailbox_read read;
+ etch_mailbox_read_withwait read_withwait;
+ etch_mailbox_close_delivery close_delivery;
+ etch_mailbox_close_read close_read;
+ etch_mailbox_register_notify register_notify;
+ etch_mailbox_unregister_notify unregister_notify;
+ etch_mailbox_is_empty is_empty;
+ etch_mailbox_is_closed is_closed;
+ etch_mailbox_is_full is_full;
+ etch_mailbox_get_message_id get_message_id;
+
+} i_mailbox;
+
+
+
+/* - - - - - - - - - - - - - - - - -
+ * public methods
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_mailbox_element()
+ * etch_mailbox_element constructor
+ * @param msg todo document ownership
+ * @param whofrom todo document ownership
+ */
+etch_mailbox_element* new_mailbox_element(etch_message* msg, etch_who* whofrom);
+
+
+/**
+ * new_default_mailbox_interface()
+ * i_mailbox constructor
+ * populates all virtuals with stub methods.
+ * @param thisx instantiator object
+ */
+i_mailbox* new_default_mailbox_interface(void* thisx);
+
+
+/**
+ * etch_mailbox_get_implobj()
+ * verify and return the concrete mailbox implementation
+ */
+etch_mailbox* etchmbox_get_implobj(i_mailbox*);
+
+
+/**
+ * etch_mailbox_get_manager()
+ * verify and return the mailbox's mailbox manager
+ */
+struct i_mailbox_manager* etchmbox_get_manager(i_mailbox*);
+
+
+/**
+ * new_mailbox_interface()
+ * i_mailbox constructor
+ * populates virtuals with specified methods.
+ * @param thisx instantiator object.
+ * @param balance of parameters are pointers to virtual method implementations.
+ */
+i_mailbox* new_mailbox_interface(void* thisx,
+ etch_mailbox_message,
+ etch_mailbox_read,
+ etch_mailbox_read_withwait,
+ etch_mailbox_close_delivery,
+ etch_mailbox_close_read,
+ etch_mailbox_register_notify,
+ etch_mailbox_unregister_notify,
+ etch_mailbox_is_empty,
+ etch_mailbox_is_closed,
+ etch_mailbox_is_full,
+ etch_mailbox_get_message_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHIMAILBOX_H */
diff --git a/binding-c/runtime/c/include/etch_mailbox_manager.h b/binding-c/runtime/c/include/etch_mailbox_manager.h
new file mode 100644
index 0000000..8ad9722
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_mailbox_manager.h
@@ -0,0 +1,132 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_mailboxmgr.h
+ * i_mailboxmgr interface
+ */
+#ifndef ETCHIMAILBOXMGR_H
+#define ETCHIMAILBOXMGR_H
+
+#if(0)
+
+ MAILBOXMANAGER
+ | transportCall(Who to, message)
+ | redeliver(Who from, message)
+ | unregister(mailbox)
+ |- SESSIONMESSAGE<SessionData>
+ | | sessionMessage(Who from, message)
+ | - SESSION
+ | sessionQuery(); sessionControl(); sessionNotify();
+ - TRANSPORTMESSAGE
+ | transportMessage(to, Message);
+ - TRANSPORT
+ transportQuery(); transportControl(); transportNotify();
+ getSession(); setSession();
+#endif
+
+#include "etch_mailbox.h"
+#include "etch_session_message.h"
+#include "etch_transport_message.h"
+#include "etch_mutex.h"
+#include "etch_object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*etch_mbm_transport_call) (struct i_mailbox_manager* thisx, etch_who* whoto, etch_message* msg, i_mailbox** out);
+
+typedef int (*etch_mbm_redeliver) (void* thisx, etch_who* whofrom, etch_message* msg);
+typedef int (*etch_mbm_unregister) (void* thisx, i_mailbox* mailbox);
+
+/**
+ * i_mailbox_manager
+ * mailbox manager interface
+ */
+typedef struct i_mailbox_manager
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+
+ /**
+ * transport_call()
+ * sends a message which begins a call after allocating a mailbox
+ * to receive responses.
+ * @param whoto recipient. caller retains.
+ * @param message the message which begins the call.
+ * caller relinquishes on success, retains on other than success.
+ * @param out pointer to location to return the i_mailbox which will
+ * receive responses to the call.
+ * @return 0 success, -1 error
+ */
+ etch_mbm_transport_call transport_call;
+
+ /**
+ * redeliver()
+ * redelivers messages extant from a closed mailbox.
+ * @param whofrom sender. caller retains.
+ * @param message. caller relinquishes on success, retains on other than success.
+ * @return 0 success, -1 error
+ */
+ etch_mbm_redeliver redeliver;
+
+ /**
+ * unregister()
+ * removes specified mailbox from the set of mailboxes receiving responses to messages.
+ * @param mailbox the mailbox to remove. caller does not own it.
+ * @return 0 success, -1 error
+ */
+ etch_mbm_unregister unregister;
+
+ /* implements session message interface */
+ i_sessionmessage* ism;
+ etch_session_message session_message;
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+ /* implements transport message interface */
+ i_transportmessage* itm;
+ etch_transport_message transport_message;
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ etch_mutex* rwlock; /* not owned */
+
+} i_mailbox_manager;
+
+
+/**
+ * new_mailboxmgr_interface()
+ * i_mailbox_manager constructor.
+ * @param thisx the mailbox manager object.
+ * @param itm transportmesssage interface, caller retains, can be null.
+ * @param ism sessionmessage interface, caller retains, can be null.
+ */
+i_mailbox_manager* new_mailboxmgr_interface(void* thisx, i_transportmessage*, i_sessionmessage*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHIMAILBOXMGR_H */
diff --git a/binding-c/runtime/c/include/etch_map.h b/binding-c/runtime/c/include/etch_map.h
new file mode 100644
index 0000000..bcc306a
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_map.h
@@ -0,0 +1,71 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etchmap.h -- generic string to object map
+ */
+
+#ifndef ETCHMAP_H
+#define ETCHMAP_H
+
+#include "etch.h"
+#include "etch_hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCHMAP_MAX_IKEYSIZE 31
+
+void* etchmap_find (etch_hashtable*, const unsigned int key, void** out);
+void* etchmap_findx (etch_hashtable*, char* key, void** out);
+void* etchmap_findxw (etch_hashtable*, wchar_t* key, void** out);
+void* etchmap_findxl (etch_hashtable*, char* key, unsigned keylen, void** out);
+void* etchmap_find_by_hash (etch_hashtable*, const unsigned hash, void** out);
+
+void* etchmap_del (etch_hashtable*, const unsigned int key);
+void* etchmap_delx (etch_hashtable*, char* key);
+void* etchmap_delxw (etch_hashtable*, wchar_t* key);
+void* etchmap_delxl (etch_hashtable*, char* ckey, const unsigned keylen);
+
+int etchmap_add (etch_hashtable*, const unsigned int key, void* data);
+int etchmap_addx (etch_hashtable*, char* key, void* data);
+int etchmap_addxw (etch_hashtable*, wchar_t* key, void* data);
+int etchmap_insert (etch_hashtable*, const unsigned, void*, const int is_check);
+int etchmap_insertx (etch_hashtable*, char* key, void* data, const int is_check);
+int etchmap_insertxw (etch_hashtable*, wchar_t* key, void* data, const int is_check);
+int etchmap_insertxl (etch_hashtable*, char*, const unsigned, void*, const int);
+int etchmap_insertxlw (etch_hashtable*, wchar_t*, const unsigned, void*, const int);
+
+int etchmap_count(etch_hashtable*);
+
+etch_hashitem* etchmap_current(etch_hashtable*);
+
+int etchmap_map_add (etch_hashtable* map, etch_object* key, etch_object* value);
+int etchmap_map_find(etch_hashtable* map, etch_object* key, etch_hashitem** out);
+int etchmap_set_add (etch_hashtable* set, etch_object* key);
+int etchmap_is_object_key(etch_hashtable*);
+
+int string_to_etchobject_clear_handler (char* key, etch_object* value);
+int string_to_genericobject_clear_handler (char* key, void* value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHMAP_H*/
diff --git a/binding-c/runtime/c/include/etch_mem.h b/binding-c/runtime/c/include/etch_mem.h
new file mode 100644
index 0000000..65dcd9a
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_mem.h
@@ -0,0 +1,59 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etchmem.h -- heap memory allocate and free.
+ * The c binding wraps the heap allocator in order to track allocations. we supply
+ * the etch_malloc macro which, when ETCH_DEBUGALLOC is defined, will accept module
+ * name and code line number, along with object type and allocation size, in order
+ * to track heap allocations and frees, and to subsequently report memory leaks.
+ */
+
+#ifndef ETCHMEM_H
+#define ETCHMEM_H
+
+#include "etch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef apr_pool_t etch_pool_t;
+
+#define etch_malloc(n,o) _etch_malloc(n, __FILE__, __LINE__)
+#define etch_realloc(p,n,o) _etch_realloc(p,n,__FILE__, __LINE__)
+#define etch_free(n) _etch_free(n, __FILE__, __LINE__)
+#define etch_showmem(f,c)
+#define etch_dumpmem()
+
+void* _etch_malloc(size_t size, char* file, int line);
+void* _etch_realloc(void*, size_t size, char* file, int line);
+void _etch_free(void* mem, char* file, int line);
+
+/* For own malloc/free hook */
+typedef void*(*mallocFunc)(size_t);
+typedef void(*freeFunc)(void*);
+typedef void*(*reallocFunc)(void*,size_t);
+
+void etch_set_mallocator(mallocFunc myMallocFunc, freeFunc myFreeFunc, reallocFunc myReallocFunc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHMEM_H*/
diff --git a/binding-c/runtime/c/include/etch_message.h b/binding-c/runtime/c/include/etch_message.h
new file mode 100644
index 0000000..1c22de1
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_message.h
@@ -0,0 +1,85 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_message.h
+ * a message is modeled as a command and some arguments. command is an arbitrary
+ * integer value, and the arguments are key / value pairs, where the key is an
+ * arbitrary integer value and the value is any one of the standard java objects
+ * an array value, a struct value, or any type serializable by value factory.
+ */
+
+#ifndef ETCHMESSAGE_H
+#define ETCHMESSAGE_H
+
+#include "etch_structval.h"
+#include "etch_value_factory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * etch_message
+ * this class is "final"
+ */
+typedef struct etch_message
+{
+ etch_object object;
+
+ etch_structvalue* sv; /* owned */
+ etch_value_factory* vf; /* not owned */
+
+} etch_message;
+
+
+/*
+ * etch_unwanted_message
+ */
+typedef struct etch_unwanted_message
+{
+ etch_object object;
+
+ etch_message* message; /* owned */
+ etch_who* whofrom; /* not owned */
+
+} etch_unwanted_message;
+
+etch_unwanted_message* new_unwanted_message(etch_who* whofrom, etch_message* msg);
+
+
+etch_message* new_message (etch_type*, const int, etch_value_factory*);
+
+etch_object* message_get (etch_message*, etch_field* key);
+etch_object* message_remove(etch_message*, etch_field* key);
+int message_put (etch_message*, etch_field* key, etch_object* value);
+int message_putc (etch_message*, etch_field* key, void** valref);
+etch_message* message_reply (etch_message*, etch_type* type);
+etch_type* message_type (etch_message*);
+char* message_aname (etch_message*);
+etch_int64* message_get_id(etch_message*);
+etch_int64* message_get_in_reply_to(etch_message*);
+int message_set_in_reply_to(etch_message*, etch_int64* id);
+int message_set_id(etch_message*, etch_int64* id);
+int message_size (etch_message*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHMESSAGE_H*/
diff --git a/binding-c/runtime/c/include/etch_messagizer.h b/binding-c/runtime/c/include/etch_messagizer.h
new file mode 100644
index 0000000..80caa82
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_messagizer.h
@@ -0,0 +1,133 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_msgizer.h
+ * messagizer accepts packets and translates them to messages,
+ * and it accepts messages and translates them to packets.
+ */
+
+#ifndef ETCHMSGIZER_H
+#define ETCHMSGIZER_H
+
+#include "apr_thread_proc.h"
+#include "etch_session_message.h"
+#include "etch_session_packet.h"
+#include "etch_transport_message.h"
+#include "etch_transport_packet.h"
+
+#include "etch_tagdata_inp.h"
+#include "etch_tagdata_out.h"
+
+#include "etch_flexbuffer.h"
+#include "etch_resources.h"
+#include "etch_mutex.h"
+#include "etch_url.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if(0)
+
+ MESSAGIZER(TRANSPORTPACKET)
+ | tagdata_input* tdi;
+ | tagdata_output* tdo;
+ | transportPacket* transport;
+ | sessionPacket(from, buf); // override i_sessionpacket
+ | transportMessage(to, Message); // implement i_transportmsg
+ - SESSIONPACKET
+ | | int (*etch_session_packet) (void* thisx, void* whofrom, void* buffer);
+ | - SESSION
+ | sessionQuery(); sessionControl(); sessionNotify();
+ - TRANSPORTMESSAGE
+ | int transportMessage(to, Message);
+ - TRANSPORT
+ transportQuery(); transportControl(); transportNotify();
+#endif
+
+
+/*
+ * etch_messagizer
+ */
+typedef struct etch_messagizer
+{
+ etch_object object;
+
+ /* i_transportpacket of next lower layer (packetizer) */
+ i_transportpacket* transport; /* not owned */
+
+ /* i_sessionmessage of next higher layer (mailbox manager) */
+ i_sessionmessage* session; /* not owned */
+
+ etch_flexbuffer* msgbuf; /* owned */
+ tagged_data_input* tdi; /* owned */
+ tagged_data_output* tdo; /* owned */
+
+ /* - - - - - - - - - - - - - - -
+ * i_transportmessage
+ * - - - - - - - - - - - - - - -
+ */
+
+ /**
+ * i_transportmessage::transport_message()
+ * delivers message to transport from session
+ * @param to recipient
+ * @return 0 success, -1 error
+ */
+ etch_transport_message transport_message;
+ i_transportmessage* transportmsg; /* owned */
+ etch_transport_control transport_control; /* i_transportmessage::itransport */
+ etch_transport_notify transport_notify; /* i_transportmessage::itransport */
+ etch_transport_query transport_query; /* i_transportmessage::itransport */
+ etch_transport_get_session get_session; /* i_transportmessage::itransport */
+ etch_transport_set_session set_session; /* i_transportmessage::itransport */
+
+ /* - - - - - - - - - - - - - - -
+ * i_sessionpacket
+ * - - - - - - - - - - - - - - -
+ */
+
+ /**
+ * i_sessionpacket::session_packet()
+ * delivers data to the session from the transport
+ * @param from from who sent the packet
+ * @param buffer the packet from the packet source
+ * @return 0 success, -1 error
+ */
+ etch_session_packet session_packet;
+ i_sessionpacket* sessionpkt; /* owned */
+ etch_session_control session_control; /* i_sessionpacket::isession */
+ etch_session_notify session_notify; /* i_sessionpacket::isession */
+ etch_session_query session_query; /* i_sessionpacket::isession */
+
+ etch_mutex* msglock;
+
+} etch_messagizer;
+
+
+etch_messagizer* new_messagizer (i_transportpacket*, wchar_t* uri, etch_resources* resxmap);
+etch_messagizer* new_messagizer_a(i_transportpacket*, etch_url*, etch_resources*);
+i_session* etch_msgizer_get_session(void*);
+void etch_msgizer_set_session(void*, void*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHMSGIZER_H */
diff --git a/binding-c/runtime/c/include/etch_msgutl.h b/binding-c/runtime/c/include/etch_msgutl.h
new file mode 100644
index 0000000..d85fd7e
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_msgutl.h
@@ -0,0 +1,70 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etchmsgutl.h -- includes common to the etch message components
+ */
+
+#ifndef ETCHMSGUTL_H
+#define ETCHMSGUTL_H
+
+#include "etch_object.h"
+#include "etch_type.h"
+#include "etch_field.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_ARRAYVALUE_DEFAULT_INITSIZE 32
+#define ETCH_ARRAYVALUE_DEFAULT_DELTSIZE 0
+#define ETCH_ARRAYVALUE_DEFAULT_READONLY TRUE
+#define ETCH_ARRAYVALUE_DEFAULT_SYNCHRONIZED FALSE
+#define ETCH_ARRAYVALUE_DEFAULT_CONTENT_TYPE ETCHARRAYLIST_CONTENT_OBJECT
+
+#define ETCH_STRUCT_CONTENT_TYPE_OBJ 1
+#define ETCH_STRUCT_DEFAULT_INITSIZE 64
+#define ETCH_STRUCT_DEFAULT_READONLY_KEY TRUE
+#define ETCH_STRUCT_DEFAULT_READONLY_VAL TRUE
+#define ETCH_STRUCT_DEFAULT_TRACKED_MEM TRUE
+#define ETCH_STRUCT_DEFAULT_CONTENT_TYPE ETCH_STRUCT_CONTENT_TYPE_OBJ
+
+/**
+ * equate java array element to an object.
+ * we do so to facilitate porting of the java code.
+ */
+typedef etch_object ETCH_ARRAY_ELEMENT;
+
+
+/**
+ * etch_struct_element
+ */
+typedef struct etch_struct_element
+{
+ etch_field* key;
+ etch_object* value;
+} etch_struct_element;
+
+
+etch_struct_element* new_struct_element(etch_field*, etch_object*, etch_struct_element*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHMSGUTL_H */
diff --git a/binding-c/runtime/c/include/etch_mutex.h b/binding-c/runtime/c/include/etch_mutex.h
new file mode 100644
index 0000000..ccecf41
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_mutex.h
@@ -0,0 +1,85 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_mutex.h -- thread mutex implementation
+ * currently wraps APR mutex.
+ */
+
+#ifndef _ETCH_MUTEX_H_
+#define _ETCH_MUTEX_H_
+
+#include "etch_errno.h"
+#include "etch_mem.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_MUTEX_NESTED 0x1
+#define ETCH_MUTEX_UNNESTED 0x2
+
+/**
+ * etch mutex type
+ */
+typedef struct etch_mutex_t etch_mutex_t;
+
+typedef etch_mutex_t etch_mutex;
+typedef int (*etch_mutex_hookproc)(int action, etch_mutex* mutex);
+
+/**
+ * create a new etch mutex instance.
+ * @param address where the new created mutex will be stored.
+ * @param flags mutex type ETCH_MUTEX_NESTED or ETCH_MUTEX_UNNESTED.
+ * @param pool memory pool where the memory will be allocated from.
+ * @return status
+ */
+etch_status_t etch_mutex_create(etch_mutex_t** mutex, unsigned int flags, etch_pool_t* pool);
+
+/**
+ * acquire the lock for the given mutex.
+ * @param mutex
+ * @return status
+ */
+etch_status_t etch_mutex_lock(etch_mutex_t* mutex);
+
+/**
+ * try to acquire the lock for the given mutex.
+ * @param mutex
+ * @return status
+ */
+etch_status_t etch_mutex_trylock(etch_mutex_t* mutex);
+
+/**
+ * release the lock for the given mutex.
+ * @param mutex
+ * @return status
+ */
+etch_status_t etch_mutex_unlock(etch_mutex_t* mutex);
+
+/**
+ * destroy the given mutex.
+ * @param mutex
+ */
+etch_status_t etch_mutex_destroy(etch_mutex_t* mutex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_MUTEX_H */
diff --git a/binding-c/runtime/c/include/etch_nativearray.h b/binding-c/runtime/c/include/etch_nativearray.h
new file mode 100644
index 0000000..cb6a36c
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_nativearray.h
@@ -0,0 +1,101 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_nativearray.h -- etch_nativarray implementation.
+ */
+#ifndef ETCH_NATIVEARRAY_H
+#define ETCH_NATIVEARRAY_H
+
+/**
+ * etch_nativearray
+ * object wrapper and methods for an n-dimensioned array of any type
+ * represented in memory as a single byte vector.
+ */
+
+#include "etch_object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// etch_nativearray defines
+#define ETCH_MAX_NATIVE_DIM 3 /* arbitrary limit on native array dimensions */
+
+/**
+ * etch_nativearray
+ */
+typedef struct etch_nativearray
+{
+ etch_object object;
+
+ unsigned char is_content_owned;
+
+ void* values; /* flattened array content */
+ unsigned short content_obj_type; /* ETCHTYPEB_INT32 means content is int */
+ unsigned short content_class_id; /* CLASSID_NONE means content not wrapped */
+ int numdims; /* number of dimensions, e.g., 2 for x[3][4] */
+
+ /* this object may be masked by etch_collection_mask to determine content
+ * type and class, so do not add any fields above this comment */
+
+ size_t itemsize; /* size in bytes of an item, e.g. sizeof(int) */
+ size_t bytecount; /* length in bytes of array content (values) */
+ size_t dimension[ETCH_MAX_NATIVE_DIM]; /* for int x[2][3] [0] is 3, [1] is 2 */
+ size_t dimsize [ETCH_MAX_NATIVE_DIM]; /* for int x[2][3] [0] is 4, [1] is 12 */
+ size_t counts [ETCH_MAX_NATIVE_DIM]; /* optional actual population counts */
+
+ // dim size example
+ // int[2][3] array 2 * 3
+ //
+ // int[0][0] 4
+ // int[0][1] 8
+ // int[0][2] 12
+ // int[1][0] 16
+ // int[1][1] 20
+ // int[1][2] 24
+ // | dim size 0 = 4 | dim size 1 = 12 = 3 * sizeof(int)
+ // | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 |
+
+ int (*put1) (struct etch_nativearray*, void* v, int i);
+ int (*put2) (struct etch_nativearray*, void* v, int i, int j);
+ int (*put3) (struct etch_nativearray*, void* v, int i, int j, int k);
+ int (*get1) (struct etch_nativearray*, void* v, int i);
+ int (*get2) (struct etch_nativearray*, void* v, int i, int j);
+ int (*get3) (struct etch_nativearray*, void* v, int i, int j, int k);
+} etch_nativearray;
+
+etch_nativearray* new_etch_nativearray (unsigned short class_id, const size_t itemsize, const int numdims, const int dim0, const int dim1, const int dim2);
+etch_nativearray* new_etch_nativearray_from (void* values, unsigned short class_id, const size_t itemsize, const int numdims, const int dim0, const int dim1, const int dim2);
+etch_nativearray* new_etch_nativearray_of(unsigned short content_obj_type, unsigned short content_class_id, const int numdims, const int dim0, const int dim1, const int dim2);
+etch_nativearray* new_subarray(etch_nativearray* a, const int i); etch_nativearray* etch_nativearray_assign_to(etch_nativearray*, etch_nativearray*);
+etch_object* etch_nativearray_get_element(etch_nativearray*, const int i);
+int etch_nativearray_get_component_type(etch_object* classobj, etch_component_type_params*);
+int etch_nativearray_get_wrapped_component(etch_nativearray*, const int i, etch_object** out);
+int destroy_etch_nativearray_content(etch_nativearray*);
+int destroy_etch_nativearray(void*);
+size_t etch_nativearray_off1 (etch_nativearray* a, int i);
+size_t etch_nativearray_off2 (etch_nativearray* a, int i, int j);
+size_t etch_nativearray_off3 (etch_nativearray* a, int i, int j, int k);
+void* etch_nativearray_clone(void* other);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_MUTEX_H */
diff --git a/binding-c/runtime/c/include/etch_object.h b/binding-c/runtime/c/include/etch_object.h
new file mode 100644
index 0000000..bb2db0a
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_object.h
@@ -0,0 +1,583 @@
+
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etchobj.h
+ * etch object definitions
+ */
+
+#ifndef ETCHOBJ_H
+#define ETCHOBJ_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "etch.h"
+#include "etch_mutex.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct etch_object;
+struct vtabmask;
+struct etchexception; /* declared in etchexcp.h which we can't include here */
+
+/**
+ * function signatures for object virtuals
+ */
+typedef int (*etch_object_destructor)(void*); /* should tak etch_object or etch_object */
+typedef void* (*etch_object_clone)(void*); /* should take and return etch_object or etch_object */
+typedef uint32 (*obj_gethashkey)(void*); /* should take etch_object or etch_object */
+
+typedef struct etchresult
+{
+ int resultcode;
+ int reasoncode;
+ struct etchexception* exception;
+} etchresult;
+
+/**
+ * etch_object
+ * mask over all etch objects
+ */
+typedef struct etch_object
+{
+ unsigned int hashkey; /* unique key used by a hash map */
+ unsigned short obj_type; /* type of this object */
+ unsigned short class_id; /* class identifier */
+ struct vtabmask* vtab; /* virtual function table */
+ etch_object_destructor destroy;
+ etch_object_clone clone;
+ obj_gethashkey get_hashkey; /* hash key calculation override */
+ struct etch_object* parent; /* class from which this derives */
+ etchresult* result; /* embedded result and exception */
+ unsigned int refcount; /* non-zero implies ref counted */
+ unsigned int length; /* byte count of the flat object */
+ unsigned char is_null; /* does object wrap a null value */
+ unsigned char is_copy; /* is object content not owned */
+ unsigned char is_static; /* should destructor free object */
+ unsigned char reserved; /* reserved for individual use */
+ etch_mutex_hookproc synchook; /* hook for synchronization */
+ etch_mutex_t* synclock; /* synchronization mutex */
+} etch_object;
+
+typedef struct etch_byte etch_byte;
+typedef struct etch_boolean etch_boolean;
+typedef struct etch_int8 etch_int8;
+typedef struct etch_int16 etch_int16;
+typedef struct etch_int32 etch_int32;
+typedef struct etch_int64 etch_int64;
+typedef struct etch_float etch_float;
+typedef struct etch_double etch_double;
+typedef struct etch_string etch_string;
+typedef struct etch_date etch_date;
+typedef struct etch_exception etch_exception;
+
+/**
+ * etchparentinfo
+ * object inheritance list entry
+ */
+typedef struct etchparentinfo
+{
+ union {
+ unsigned short obj_type;
+ unsigned short list_size; /* entry[0] in any inheritance list */
+ } o;
+ union {
+ unsigned short class_id;
+ unsigned short list_count; /* entry[0] in any inheritance list */
+ } c;
+} etchparentinfo;
+
+/**
+ * vtabmask
+ * mask over any vtable
+ */
+typedef struct vtabmask
+{
+ etch_object object;
+
+ // inheritance list
+ etchparentinfo* inherits_from; // 4 byte
+ unsigned char unused[4]; // 4 byte
+
+ /* function pointers start here */
+} vtabmask;
+
+
+/**
+ * etch_byte - wrapped byte
+ * note that this and all CLASSID_PRIMITIVE_XXXX wrapped primitives are expected
+ * by code which generalizes such objects, to have the object's value as the first
+ * non-header item in the object.
+ */
+struct etch_byte
+{
+ etch_object object;
+ signed char value; // 1 byte
+};
+
+/**
+ * etch_boolean - wrapped boolean
+ * note that this and all CLASSID_PRIMITIVE_XXXX wrapped primitives are expected
+ * by code which generalizes such objects, to have the object's value as the first
+ * non-header item in the object.
+ */
+struct etch_boolean
+{
+ etch_object object;
+ unsigned char value; // 1 byte
+};
+
+/**
+ * etch_int8 - wrapped 8-bit integer
+ * note that this and all CLASSID_PRIMITIVE_XXXX wrapped primitives are expected
+ * by code which generalizes such objects, to have the object's value as the first
+ * non-header item in the object.
+ */
+struct etch_int8
+{
+ etch_object object;
+ signed char value; // 1 byte
+};
+
+/**
+ * etch_int16 - wrapped 16-bit integer
+ * note that this and all CLASSID_PRIMITIVE_XXXX wrapped primitives are expected
+ * by code which generalizes such objects, to have the object's value as the first
+ * non-header item in the object.
+ */
+struct etch_int16
+{
+ etch_object object;
+ signed short value; // 2 byte
+};
+
+/**
+ * etch_int32 - wrapped 32-bit integer
+ * note that this and all CLASSID_PRIMITIVE_XXXX wrapped primitives are expected
+ * by code which generalizes such objects, to have the object's value as the first
+ * non-header item in the object.
+ */
+struct etch_int32
+{
+ etch_object object;
+ signed int value; // 4 byte
+};
+
+/**
+ * etch_int64 - wrapped 64-bit integer
+ * note that this and all CLASSID_PRIMITIVE_XXXX wrapped primitives are expected
+ * by code which generalizes such objects, to have the object's value as the first
+ * non-header item in the object.
+ */
+struct etch_int64
+{
+ etch_object object;
+ int64 value; // 8 byte
+};
+
+/**
+ * etch_float - wrapped 32-bit float
+ * note that this and all CLASSID_PRIMITIVE_XXXX wrapped primitives are expected
+ * by code which generalizes such objects, to have the object's value as the first
+ * non-header item in the object.
+ */
+struct etch_float
+{
+ etch_object object;
+ float value; // 4 byte
+};
+
+/**
+ * etch_double - wrapped 64-bit float
+ * note that this and all CLASSID_PRIMITIVE_XXXX wrapped primitives are expected
+ * by code which generalizes such objects, to have the object's value as the first
+ * non-header item in the object.
+ */
+struct etch_double
+{
+ etch_object object;
+ double value; // 8 byte
+};
+
+
+typedef union etch_charptr
+{
+ void* value;
+ wchar_t* valw;
+ char* valc;
+} etch_charptr;
+
+
+/**
+ * etch_string - wrapped pointer to unicode string
+ * note that this and all CLASSID_PRIMITIVE_XXXX wrapped primitives are expected
+ * by code which generalizes such objects, to have the object's value as the first
+ * non-header item in the object.
+ */
+struct etch_string
+{
+ etch_object object;
+ union etch_charptr v; /* pointer to string value */
+ unsigned int char_count; /* number of characters */
+ unsigned int byte_count; /* including terminator */
+ unsigned char encoding;
+};
+
+/**
+ * etch_date - date object
+ */
+struct etch_date
+{
+ etch_object object;
+
+ time_t value;
+ clock_t ticks;
+};
+
+typedef enum excptype
+{
+ EXCPTYPE_NONE = 0x0,
+ EXCPTYPE_BUILTIN = 0x1,
+ EXCPTYPE_USERDEFINED = 0x2
+} excptype_t;
+
+/**
+ * etch_collection_mask
+ * masks etch object collections such as etch_nativearray, etch_arraylist,
+ * and etch_hashtable. not instantiated.
+ */
+typedef struct etch_collection_mask
+{
+ etch_object object;
+
+ void* p;
+ unsigned short content_obj_type;
+ unsigned short content_class_id;
+ unsigned int n;
+
+} etch_collection_mask;
+
+
+/**
+ * etch_arraytype is used as a mask over etch_nativearray and etch_arrayvalue.
+ * a function using an etch_arraytype* parameter will test for one or the other
+ * and possibly convert the passed array to the other type.
+ */
+typedef struct etch_collection_mask etch_arraytype;
+
+/**
+ * etch_objclass
+ * parameter structure identifying an etch object type
+ */
+typedef struct etch_objclass
+{
+ unsigned short obj_type;
+ unsigned short class_id;
+ unsigned short content_obj_type;
+ unsigned short content_class_id;
+ unsigned short vtable_class_id;
+ unsigned int numdims;
+ etchparentinfo* inherits_from;
+ etch_object* parent;
+
+} etch_objclass;
+
+
+/**
+ * etch_component_type_params
+ * parameter and result structure for get_component_type() etc.
+ */
+typedef struct etch_component_type_params
+{
+ unsigned int dimensions;
+ unsigned short origl_class_id;
+ unsigned short origl_obj_type;
+ unsigned short final_obj_type;
+ unsigned short final_class_id;
+ struct etch_nativearray* origl_array;
+ struct etch_nativearray* final_array;
+
+} etch_component_type_params;
+
+
+typedef struct etch_array_id_params
+{
+ unsigned short array_obj_type;
+ unsigned short array_class_id;
+ unsigned short content_obj_type;
+ unsigned short content_class_id;
+
+} etch_array_id_params;
+
+
+/*
+ * default base object virtual method implementations
+ */
+int destroy_object(void*);
+int destroy_objectex(etch_object*);
+int destroy_string(void*);
+void* clone_object (void*);
+void* clone_string (void*);
+void* clone_null(void*);
+etch_object* new_object(const int objsize,
+ const unsigned short obj_type, const unsigned short class_id);
+
+/*
+ * wide char string clone
+ */
+wchar_t* new_wchar(const wchar_t* s);
+
+/*
+ * narrow char string clone
+ */
+char* new_char(const char* s);
+
+typedef struct etch_who {
+ etch_object object;
+ void* value;
+} etch_who;
+
+
+typedef etch_int32 etch_event;
+typedef etch_int32 etch_query;
+typedef etch_int32 etch_control;
+etch_who* new_who(void* whoobj);
+etch_object* new_nullobj();
+
+int destroy_etch_object_value(etch_object*);
+etch_object* etchobj_assign_to(etch_object*, etch_object*);
+
+
+/*
+ * etch primitive constructors
+ */
+etch_byte* new_byte(const signed char);
+etch_boolean* new_boolean(boolean);
+etch_int8* new_int8(signed char);
+etch_int16* new_int16(short);
+etch_int32* new_int32(int);
+etch_int64* new_int64(int64);
+etch_float* new_float(float);
+etch_double* new_double(double);
+etch_string* new_string(const void*, const unsigned char encoding);
+etch_string* new_stringw(const void*);
+etch_string* new_stringa(const void*);
+etch_string* new_string_from(const void*, const unsigned char encoding);
+etch_date* new_date();
+etch_object* new_primitive(const unsigned, const unsigned short);
+
+etchresult* new_etchresult(const int result, const int reason);
+
+int32 etch_number_as_int32(const void* object);
+
+/* these are wrapped integers for now. if we need more data, we can define dedicated objects */
+etch_event* new_etch_event (const unsigned short class_id, const int value);
+etch_query* new_etch_query (const unsigned short class_id, const int value);
+etch_control* new_etch_control(const unsigned short class_id, const int value);
+
+etch_status_t etch_object_destroy(void* object);
+etch_object* etch_object_clone_func(void* pobject);
+
+#define ETCH_NOREFCOUNT_MARKER 0xffffffff
+#define is_etchobj_refcount_decremented(x) (x->get_hashkey(x) == ETCH_NOREFCOUNT_MARKER)
+
+#define ETCHOBJCLASS(x) ETCHMAKECLASS(((etch_object*)x)->obj_type, ((etch_object*)x)->class_id)
+#define ETCH_CONX_SIG 0xe5d4c3b2
+
+typedef union
+{
+ char vbyte; unsigned char vubyte; short vint16; int vint32; int64 vint64; double vdouble;
+ float vfloat; void* vaddr; etch_string* vstring;
+ struct etch_object* vetch_object;
+ struct etch_nativearray* vnatarray; struct etch_structvalue* vsv;
+ struct etch_arrayvalue* varrayval; struct etch_arraylist* varraylist;
+ int64 all;
+} union_alltypes;
+
+
+#define is_etch_byte(x) (x && ((etch_object*)x)->class_id == CLASSID_PRIMITIVE_BYTE)
+#define is_etch_boolean(x) (x && ((etch_object*)x)->class_id == CLASSID_PRIMITIVE_BOOL)
+#define is_etch_int8(x) (x && ((etch_object*)x)->class_id == CLASSID_PRIMITIVE_INT8)
+#define is_etch_int16(x) (x && ((etch_object*)x)->class_id == CLASSID_PRIMITIVE_INT16)
+#define is_etch_int32(x) (x && ((etch_object*)x)->class_id == CLASSID_PRIMITIVE_INT32)
+#define is_etch_int64(x) (x && ((etch_object*)x)->class_id == CLASSID_PRIMITIVE_INT64)
+#define is_etch_float(x) (x && ((etch_object*)x)->class_id == CLASSID_PRIMITIVE_FLOAT)
+#define is_etch_double(x) (x && ((etch_object*)x)->class_id == CLASSID_PRIMITIVE_DOUBLE)
+#define is_etch_primitive(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_PRIMITIVE)
+#define is_etch_struct(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_STRUCTVAL)
+#define is_etch_validator(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_VALIDATOR)
+#define is_etch_arraylist(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_ARRAYLIST)
+#define is_etch_arrayvalue(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_ARRAYVAL)
+#define is_etch_valuefact(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_VALUEFACTORY)
+#define is_etch_valuefactimpl(x)(x && ((etch_object*)x)->obj_type == ETCHTYPEB_VALUEFACTIMP)
+#define is_etch_exception(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_EXCEPTION)
+#define is_etch_queue(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_ETCHQUEUE)
+#define is_etch_wait(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_WAIT)
+#define is_etch_mailbox(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_MAILBOX)
+#define is_etch_imailbox(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_MAILBOXINT)
+#define is_etch_mailboxmgr(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_MBOXMGR_IMPL)
+#define is_etch_imailboxmgr(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_MBOX_MANAGER)
+#define is_etch_sessionmsg(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_SESSIONMSG)
+#define is_etch_transportmsg(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_TRANSPORTMSG)
+#define is_etch_hashtable(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_HASHTABLE)
+#define is_etch_nativearray(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_NATIVEARRAY)
+#define is_etch_deliverysvc(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_DELIVERYSVC_IMPL)
+#define is_etch_ideliverysvc(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_DELIVERYSVCINT)
+#define is_etch_tcpserver(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_TCPSERVER)
+#define is_etch_sessionlxr(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_SESSIONLXR)
+#define is_etch_sessionpacket(x)(x && ((etch_object*)x)->obj_type == ETCHTYPEB_SESSIONPKT)
+#define is_etch_transportpkt(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_TRANSPORTPKT)
+#define is_etch_sessiondata(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_SESSIONDATA)
+#define is_etch_transportdata(x)(x && ((etch_object*)x)->obj_type == ETCHTYPEB_TRANSPORTDATA)
+#define is_etch_packetizer(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_PACKETIZER)
+#define is_etch_messagizer(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_MESSAGIZER)
+#define is_etch_serverimpl(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_EXESERVERIMPL)
+#define is_etch_serverbase(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_EXESERVERBASE)
+#define is_etch_factoryparams(x)(x && ((etch_object*)x)->obj_type == ETCHTYPEB_FACTORYPARAMS)
+#define is_etch_clientsession(x)(x && ((etch_object*)x)->obj_type == ETCHTYPEB_CLIENT_SESSION)
+#define is_etch_remote_server(x)(x && ((etch_object*)x)->obj_type == ETCHTYPEB_REMOTESERVER)
+#define is_etch_client_impl(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_EXECLIENTIMPL)
+#define is_etch_client_stub(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_CLIENTSTUB)
+#define is_etch_server_stub(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_SERVERSTUB)
+#define is_etch_client_base(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_EXECLIENTBASE)
+#define is_etch_threadpool(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_THREADPOOL)
+#define is_etch_flexbuffer(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_FLEXBUF)
+#define is_etch_message(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_MESSAGE)
+#define is_etch_thread(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_THREAD)
+
+#define is_etch_string(x) (x && ((etch_object*)x)->class_id == CLASSID_STRING \
+ && ((etch_object*)x)->obj_type == ETCHTYPEB_PRIMITIVE)
+
+#define is_etch_date(x) (x && ((etch_object*)x)->class_id == CLASSID_DATE \
+ && ((etch_object*)x)->obj_type == ETCHTYPEB_PRIMITIVE)
+
+#define is_etch_object(x) (x && ((etch_object*)x)->obj_type == ETCHTYPEB_ETCHOBJECT)
+#define is_etch_object_type(a,b)(a == ETCHTYPEB_ETCHOBJECT || b == CLASSID_OBJECT )
+#define is_etch_nativearray_type(t,c) (t == ETCHTYPEB_NATIVEARRAY)
+#define is_etch_arraylist_type(t,c) (t == ETCHTYPEB_ARRAYLIST)
+#define is_etch_objarray_type(t,c) (c == CLASSID_ARRAY_OBJECT)
+
+#define is_etch_tcpconnection(x) (x \
+ && ((etch_object*)x)->obj_type == ETCHTYPEB_CONNECTION \
+ && ((etch_object*)x)->class_id == CLASSID_TCP_CONNECTION)
+
+#define is_etch_serverparams(x) (x \
+ && ((etch_object*)x)->obj_type == ETCHTYPEB_FACTORYPARAMS \
+ && ((etch_object*)x)->class_id == CLASSID_SERVERFACTORY)
+
+#define is_etch_clientparams(x) (x \
+ && ((etch_object*)x)->obj_type == ETCHTYPEB_FACTORYPARAMS \
+ && ((etch_object*)x)->class_id == CLASSID_CLIENTFACTORY)
+
+#define is_etch_combo_validator(x) (x \
+ && ((etch_object*)x)->obj_type == ETCHTYPEB_VALIDATOR \
+ && ((etch_object*)x)->class_id == CLASSID_COMBO_VALIDATOR)
+
+#define is_etch_primitive_number(x) (x \
+ && ((etch_object*)x)->class_id >= CLASSID_PRIMITIVE_BYTE \
+ && ((etch_object*)x)->class_id <= CLASSID_PRIMITIVE_DOUBLE)
+
+#define is_etch_arraytype(x) (x \
+ && (((etch_object*)x)->obj_type >= ETCHTYPEB_NATIVEARRAY \
+ || ((etch_object*)x)->obj_type <= ETCHTYPEB_ARRAYVAL))
+
+#define is_etch_unwantedmsg(x) (x \
+ && ((etch_object*)x)->obj_type == ETCHTYPEB_EVENT \
+ && ((etch_object*)x)->class_id == CLASSID_EVENT_UNWANTMSG)
+
+
+#define is_etch_objparams(x,a,b) (x && ((etch_object*)x)->obj_type == a && ((etch_object*)x)->class_id == b)
+
+#define is_etch_connection(cx) (cx && (*(unsigned*)cx) == ETCH_CONX_SIG)
+
+/**
+ * macros to interpret state of the object byteflag is_static
+ */
+#define ETCHOBJ_IMMUTABLE_SHELL 1 /* object shell not to be freed */
+#define ETCHOBJ_IMMUTABLE_CONTENT 2 /* object content not to be freed */
+#define ETCHOBJ_IMMUTABLE_ALL 3 /* entire object not to be freed */
+#define ETCHOBJ_STATIC_RESOURCE 4 /* object will not be destroyed by resources mgr */
+#define is_etchobj_static_shell(x) ( ( ((etch_object*)x)->is_static & ETCHOBJ_IMMUTABLE_SHELL ) != 0 )
+#define is_etchobj_static_shellonly(x) ( ((etch_object*)x)->is_static == ETCHOBJ_IMMUTABLE_SHELL )
+#define is_etchobj_static_content(x) ( ((etch_object*)x)->is_static >= ETCHOBJ_IMMUTABLE_CONTENT )
+#define is_etchobj_static_contonly(x) ( ((etch_object*)x)->is_static == ETCHOBJ_IMMUTABLE_CONTENT )
+#define is_etchobj_static_all(x) ( ((etch_object*)x)->is_static == ETCHOBJ_IMMUTABLE_ALL )
+#define is_etchobj_static_resource(x) ( ( ((etch_object*)x)->is_static & ETCHOBJ_STATIC_RESOURCE) != 0 )
+#define set_etchobj_static_all(x) ( ((etch_object*)x)->is_static |= ETCHOBJ_IMMUTABLE_ALL )
+#define set_etchobj_static_shell(x) ( ((etch_object*)x)->is_static |= ETCHOBJ_IMMUTABLE_SHELL )
+#define set_etchobj_static_content(x) ( ((etch_object*)x)->is_static |= ETCHOBJ_IMMUTABLE_CONTENT )
+#define set_etchobj_static_resource(x) ( ((etch_object*)x)->is_static |= ETCHOBJ_STATIC_RESOURCE )
+#define clear_etchobj_static_shell(x) ( ((etch_object*)x)->is_static &= ~ETCHOBJ_IMMUTABLE_SHELL )
+#define clear_etchobj_static_content(x) ( ((etch_object*)x)->is_static &= ~ETCHOBJ_IMMUTABLE_CONTENT )
+#define clear_etchobj_static_all(x) ( ((etch_object*)x)->is_static &= ~ETCHOBJ_IMMUTABLE_ALL )
+#define clear_etchobj_static_resource(x)( ((etch_object*)x)->is_static &= ~ETCHOBJ_STATIC_RESOURCE )
+
+
+/**
+ * macros to get size/count from etch object inheritance list
+ */
+#define has_parents(obj) {obj && ((vtabmask*)((etch_object*)obj)->vtab)->inherits_from \
+ && ((vtabmask*)((etch_object*)obj)->vtab)->inherits_from[0].list_count > 0))
+#define get_etchobj_parent_count(obj) \
+ (obj && ((etch_object*)obj)->vtab && ((vtabmask*)obj->vtab)->inherits_from? \
+ ((vtabmask*)((etch_object*)obj)->vtab)->inherits_from[0].list_count: 0)
+#define get_etchobj_parent_listsize(obj) \
+ (obj && ((etch_object*)obj)->vtab && ((vtabmask*)obj->vtab)->inherits_from? \
+ ((vtabmask*)((etch_object*)obj)->vtab)->inherits_from[0].list_size: 0)
+
+/**
+ * methods to access and/or instantiate object's inheritance hierarchy
+ */
+etchparentinfo* get_vtab_inheritance_list(etch_object*,
+ const short size, const short count, const short vtabclass);
+etchparentinfo* new_etch_inheritance_list(const short size, const short count,
+ etchparentinfo* oldlist);
+etchparentinfo* get_next_etch_parent(etch_object*, int current_index);
+etchparentinfo* get_next_etch_parentex(const unsigned short class_id,
+ etchparentinfo* inhertlist, int current_index);
+
+int etchobj_is_assignable_from(etch_objclass* target, etch_objclass* source);
+int etchobj_is_assignable_fromobj(etch_object* targetobj, etch_object* sourceobj);
+void set_etch_assignable_arg_from(etch_objclass*, etch_object*);
+
+unsigned etch_get_char_hashkey (const char*);
+unsigned etch_get_wchar_hashkey(const wchar_t*);
+
+int is_derives_from_object(etch_object*);
+int is_derives_from_object_class(const unsigned short class_id);
+int verify_object(etch_object*, const unsigned short, const unsigned short, void** out);
+void* new_vtable(const void* parentvtab, const size_t size, const short classid);
+short short_type(unsigned i, unsigned j);
+void* get_base_vtable(etch_object*);
+unsigned etch_addref(etch_object*);
+unsigned etch_release(etch_object*);
+unsigned etch_release_wrapper(etch_object*);
+uint32 defgethashkey(void*);
+uint32 etch_number_get_hashkey(void*);
+unsigned get_vtable_cachehkey(unsigned short class_id);
+unsigned get_class_cachekey(unsigned short obj_type, unsigned short class_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHOBJ_H */
diff --git a/binding-c/runtime/c/include/etch_objecttypes.h b/binding-c/runtime/c/include/etch_objecttypes.h
new file mode 100644
index 0000000..e276f94
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_objecttypes.h
@@ -0,0 +1,361 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_objecttypes.h -- constants for internal object types.
+ */
+
+#ifndef ETCHOBJTYPES_H
+#define ETCHOBJTYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * identifies a class object's data type or content type
+ */
+typedef enum objtype_b
+{
+ ETCHTYPEB_UNDEFINED = 0x0,
+ ETCHTYPEB_NONE = 0x0,
+ ETCHTYPEB_BYTE = 0x1,
+ ETCHTYPEB_BOOL = 0x2,
+ ETCHTYPEB_INT8 = 0x3,
+ ETCHTYPEB_INT16 = 0x4,
+ ETCHTYPEB_INT32 = 0x5,
+ ETCHTYPEB_INT64 = 0x6,
+ ETCHTYPEB_IEEE32 = 0x7,
+ ETCHTYPEB_IEEE64 = 0x8,
+ ETCHTYPEB_STRING = 0x9,
+ ETCHTYPEB_CLASS = 0xa,
+ ETCHTYPEB_RAWOBJECT = 0xb,
+ ETCHTYPEB_CUSTOM = 0xc,
+ ETCHTYPEB_EXTERN = 0xd,
+ ETCHTYPEB_ETCHOBJECT = 0xe,
+ ETCHTYPEB_HASHTABLE = 0xf,
+ ETCHTYPEB_VTABLE = 0x10,
+ ETCHTYPEB_EXCEPTION = 0x11,
+ ETCHTYPEB_CACHEREC = 0x12,
+ ETCHTYPEB_BYTES = 0x13,
+ ETCHTYPEB_ID_NAME = 0x14,
+ ETCHTYPEB_FIELD = 0x15,
+ ETCHTYPEB_TYPE = 0x16,
+ ETCHTYPEB_STRUCTVAL = 0x17,
+ ETCHTYPEB_ARRAYVAL = 0x18,
+ ETCHTYPEB_VALUEFACTORY = 0x19,
+ ETCHTYPEB_VALUEFACTOBJ = 0x1a,
+ ETCHTYPEB_VALUEFACTIMP = 0x1b,
+ ETCHTYPEB_MESSAGE = 0x1c,
+ ETCHTYPEB_TAGDATA = 0x1d,
+ ETCHTYPEB_TAGDATAINP = 0x1e,
+ ETCHTYPEB_TAGDATAOUT = 0x1f,
+ ETCHTYPEB_TDIOBJ = 0x20,
+ ETCHTYPEB_TDOOBJ = 0x21,
+ ETCHTYPEB_ARRAYELEMENT = 0x22,
+ ETCHTYPEB_STRUCTELEMENT = 0x23,
+ ETCHTYPEB_INSTANCEDATA = 0x24,
+ ETCHTYPEB_COLLECTION = 0x25,
+ ETCHTYPEB_LINKLIST = 0x26,
+ ETCHTYPEB_ITERATOR = 0x27,
+ ETCHTYPEB_RESULT = 0x28,
+ ETCHTYPEB_PRIMITIVE = 0x29,
+ ETCHTYPEB_DATE = 0x2a,
+ ETCHTYPEB_URL = 0x2b,
+ ETCHTYPEB_XPORTFACT = 0x2c,
+ ETCHTYPEB_CONNECTION = 0x2d,
+ ETCHTYPEB_TCPSERVER = 0x2e,
+ ETCHTYPEB_TCPCLIENT = 0x2f,
+ ETCHTYPEB_SOCKET = 0x30,
+ ETCHTYPEB_FLEXBUF = 0x31,
+ ETCHTYPEB_WHO = 0x32,
+ ETCHTYPEB_ARRAYLIST = 0x33,
+ ETCHTYPEB_BINARYTDI = 0x34,
+ ETCHTYPEB_BINARYTDO = 0x35,
+ ETCHTYPEB_IDNAMEIMPL = 0x36,
+ ETCHTYPEB_SERIALIZER = 0x37,
+ ETCHTYPEB_FORMATFACT = 0x38,
+ ETCHTYPEB_VALIDATOR = 0x39,
+ ETCHTYPEB_NATIVEARRAY = 0x3a,
+ ETCHTYPEB_SESSIONMSG = 0x3b,
+ ETCHTYPEB_TRANSPORTMSG = 0x3c,
+ ETCHTYPEB_SESSIONDATA = 0x3d,
+ ETCHTYPEB_SESSIONPKT = 0x3e,
+ ETCHTYPEB_SESSIONLXR = 0x3f,
+ ETCHTYPEB_DEFAULT_VF = 0x40,
+ ETCHTYPEB_DEFAULT_VFOBJ = 0x41,
+ ETCHTYPEB_DEFAULT_VFIMP = 0x42,
+ ETCHTYPEB_THREAD = 0x43,
+ ETCHTYPEB_THREADPOOL = 0x44,
+ ETCHTYPEB_MUTEX = 0x45,
+ ETCHTYPEB_THREADPARAMS = 0x46,
+ ETCHTYPEB_WAIT = 0x47,
+ ETCHTYPEB_OBJSESSION = 0x48,
+ ETCHTYPEB_MSGHANDLER = 0x49,
+ ETCHTYPEB_SOURCE = 0x4a,
+ ETCHTYPEB_SOURCEHDLR = 0x4b,
+ ETCHTYPEB_MSGSOURCE = 0x4c,
+ ETCHTYPEB_PACKETIZER = 0x4d,
+ ETCHTYPEB_MESSAGIZER = 0x4e,
+ ETCHTYPEB_PACKETHANDLER = 0x4f,
+ ETCHTYPEB_ETCHLIST = ETCHTYPEB_ARRAYLIST,
+ ETCHTYPEB_ETCHMAP = ETCHTYPEB_HASHTABLE,
+ ETCHTYPEB_ETCHSET = 0x50,
+ ETCHTYPEB_ETCHQUEUE = 0x51,
+ ETCHTYPEB_UNUSED1 = 0x52,
+ ETCHTYPEB_SERVERIMPL = 0x53,
+ ETCHTYPEB_EVENT = 0x54,
+ ETCHTYPEB_MAILBOX = 0x55,
+ ETCHTYPEB_MBOX_ELEMENT = 0x56,
+ ETCHTYPEB_MBOX_MANAGER = 0x57,
+ ETCHTYPEB_MBOXMGR_IMPL = 0x58,
+ ETCHTYPEB_MAILBOXINT = 0x59,
+ ETCHTYPEB_TRANSPORTDATA = 0x5b,
+ ETCHTYPEB_TRANSPORTPKT = 0x5c,
+ ETCHTYPEB_DELIVERYSVC = 0x5d,
+ ETCHTYPEB_DELIVERYSVCINT = 0x5e,
+ ETCHTYPEB_DELIVERYSVC_IMPL= 0x5f,
+ ETCHTYPEB_SERVERFACT = 0x60,
+ ETCHTYPEB_CLIENTFACT = 0x61,
+ ETCHTYPEB_SERVERFACT_IMPL = 0x62,
+ ETCHTYPEB_CLIENTFACT_IMPL = 0x63,
+ ETCHTYPEB_SVCINTERFACE = 0x64,
+ ETCHTYPEB_EXESERVERBASE = 0x66,
+ ETCHTYPEB_EXESERVERIMPL = 0x67,
+ ETCHTYPEB_EXECLIENTBASE = 0x68,
+ ETCHTYPEB_EXECLIENTIMPL = 0x69,
+ ETCHTYPEB_REMOTE = 0x6a,
+ ETCHTYPEB_REMOTECLIENT = 0x6b,
+ ETCHTYPEB_REMOTESERVER = 0x6c,
+ ETCHTYPEB_CLIENT_SESSION = 0x74,
+ ETCHTYPEB_STUB = 0x70,
+ ETCHTYPEB_CLIENTSTUB = 0x71,
+ ETCHTYPEB_SERVERSTUB = 0x72,
+ ETCHTYPEB_FACTORYPARAMS = 0x73,
+
+ ETCHTYPEB_EODMARK = 0x7f,
+
+ ETCHTYPEB_DYNAMIC = 0x80,
+
+ ETCHTYPEB_USER = 0xa0,
+
+} objtype_b;
+
+const char* etch_object_type_get_name(objtype_b type);
+
+/**
+ * class IDs
+ */
+typedef enum etch_classid
+{
+ CLASSID_NONE = 0x0,
+ CLASSID_ANY = 0x0,
+ CLASSID_UNWRAPPED = 0x0,
+ CLASSID_PRIMITIVE_BYTE = 0x1, /* primitive class IDs must start at 1 */
+ CLASSID_PRIMITIVE_BOOL = 0x2,
+ CLASSID_PRIMITIVE_INT8 = 0x3,
+ CLASSID_PRIMITIVE_INT16 = 0x4,
+ CLASSID_PRIMITIVE_INT32 = 0x5,
+ CLASSID_PRIMITIVE_INT64 = 0x6,
+ CLASSID_PRIMITIVE_FLOAT = 0x7,
+ CLASSID_PRIMITIVE_DOUBLE = 0x8,
+ CLASSID_STRING = 0x9,
+ CLASSID_DATE = 0xa,
+ CLASSID_OBJECT = 0xf,
+ CLASSID_DEF_VF = 0x10,
+ CLASSID_DEF_VF_OBJ = 0x11,
+ CLASSID_DEF_VF_IMPL = 0x12,
+ CLASSID_DEF_VF_VTAB = 0x13,
+ CLASSID_BINARYTDI_VTAB = 0x14,
+ CLASSID_BINARYTDO_VTAB = 0x15,
+ CLASSID_TAGDATA = 0x16,
+ CLASSID_BINARYTDI = 0x17,
+ CLASSID_BINARYTDO = 0x18,
+ CLASSID_ETCHMESSAGE = 0x1a,
+ CLASSID_HASHTABLE = 0x1b,
+ CLASSID_ETCH_MAP = CLASSID_HASHTABLE,
+ CLASSID_HASHTABLE_VTAB = 0x20,
+ CLASSID_ITERABLE_VTAB = 0x21,
+ CLASSID_ITERATOR = 0x22,
+ CLASSID_ID_NAME = 0x23,
+ CLASSID_ID_FIELD = 0x24,
+ CLASSID_ID_TYPE = 0x25,
+ CLASSID_TDI_VTAB = 0x26,
+ CLASSID_TDO_VTAB = 0x27,
+ CLASSID_BINTDI_VTAB = 0x28,
+ CLASSID_BINTDO_VTAB = 0x29,
+ CLASSID_AVAILABLE_VTAB = 0x2a,
+ CLASSID_TYPEIMPL = 0x2b,
+ CLASSID_VTAB_FIELD = 0x2c,
+ CLASSID_VTAB_TYPE = 0x2d,
+ CLASSID_ETCH_ARRAYLIST = 0x2e,
+ CLASSID_ETCH_LIST = CLASSID_ETCH_ARRAYLIST,
+ CLASSID_ETCH_SET = 0x2f,
+ CLASSID_STRUCTVALUE = 0x30,
+ CLASSID_ARRAYVALUE = 0x31,
+ CLASSID_ARRAYELEMENT = 0x32,
+ CLASSID_VALUEFACTORY = 0x33,
+ CLASSID_TAGDATAINP = 0x34,
+ CLASSID_TAGDATAOUT = 0x35,
+ CLASSID_EXCEPTION = 0x36,
+ CLASSID_THREAD = 0x39,
+ CLASSID_THREADPOOL = 0x3a,
+ CLASSID_MUTEX = 0x3b,
+ CLASSID_WAIT = 0x3c,
+ CLASSID_STUB = 0x3d,
+ CLASSID_SERVERIMPL = 0x3e,
+ CLASSID_MSGSOURCE = 0x3f,
+ CLASSID_MESSAGIZER = 0x40,
+ CLASSID_MSGHANDLER = 0x41,
+ CLASSID_SOURCE = 0x42,
+ CLASSID_SOURCEHDLR = 0x43,
+ CLASSID_URL = 0x49,
+ CLASSID_PACKETIZER = 0x4a,
+ CLASSID_PACKETHANDLER = 0x4b,
+ CLASSID_FLEXBUF = 0x4e,
+ CLASSID_WHO = 0x4f,
+ CLASSID_ARRAY_OBJECT = 0x50,
+ CLASSID_ARRAY_BYTE = 0x51,
+ CLASSID_ARRAY_BOOL = 0x52,
+ CLASSID_ARRAY_INT8 = 0x53,
+ CLASSID_ARRAY_INT16 = 0x54,
+ CLASSID_ARRAY_INT32 = 0x55,
+ CLASSID_ARRAY_INT64 = 0x56,
+ CLASSID_ARRAY_FLOAT = 0x57,
+ CLASSID_ARRAY_DOUBLE = 0x58,
+ CLASSID_ARRAY_STRING = 0x59,
+ CLASSID_ARRAY_STRUCT = 0x5a,
+ CLASSID_ETCHQUEUE = 0x5b,
+ CLASSID_ETCHSOCKET = 0x5c,
+ CLASSID_CLIENT_SESSION = 0x5d,
+ CLASSID_UNUSED_1 = 0x5e,
+ CLASSID_XPORTFACT = 0x5f,
+ CLASSID_FORMATFACT = 0x60,
+ CLASSID_FORMATFACT_BINARY = 0x61,
+ CLASSID_FORMATFACT_XML = 0x62,
+ CLASSID_TCP_CONNECTION = 0x63,
+ CLASSID_TCP_LISTENER = 0x64,
+ CLASSID_TCP_CLIENT = 0x65,
+ CLASSID_SOCKET = 0x66,
+ CLASSID_UNUSED_2 = 0x67,
+ CLASSID_UNUSED_3 = 0x68,
+ CLASSID_SESSIONMSG = 0x69,
+ CLASSID_TRANSPORTMSG = 0x6a,
+ CLASSID_TRANSPORTPKT = 0x6b,
+ CLASSID_SESSIONDATA = 0x6c,
+ CLASSID_TRANSPORTDATA = 0x6d,
+ CLASSID_SESSIONPKT = 0x6e,
+ CLASSID_SESSIONLXR = 0x6f,
+ CLASSID_VALIDATOR = 0x70,
+ CLASSID_COMBO_VALIDATOR = 0x71,
+ CLASSID_VALIDATOR_BOOL = 0x72,
+ CLASSID_VALIDATOR_BYTE = 0x73,
+ CLASSID_VALIDATOR_INT8 = 0x74,
+ CLASSID_VALIDATOR_INT16 = 0x75,
+ CLASSID_VALIDATOR_INT32 = 0x76,
+ CLASSID_VALIDATOR_INT64 = 0x77,
+ CLASSID_VALIDATOR_FLOAT = 0x78,
+ CLASSID_VALIDATOR_DOUBLE = 0x79,
+ CLASSID_VALIDATOR_STRING = 0x7a,
+ CLASSID_VALIDATOR_OBJECT = 0x7b,
+ CLASSID_VALIDATOR_EXCEPTION= 0x7c,
+ CLASSID_VALIDATOR_STRUCT = 0x7d,
+ CLASSID_VALIDATOR_EOD = 0x7e,
+ CLASSID_VALIDATOR_CUSTOM = 0x7f,
+
+ CLASSID_RUNTIME_EXCEPTION = 0x80,
+ CLASSID_AUTH_EXCEPTION = 0x81,
+ CLASSID_SERIALIZER_EXCP = 0x82,
+ CLASSID_SERIALIZER_RTXCP = 0x83,
+ CLASSID_SERIALIZER_AUTHXCP = 0x84,
+ CLASSID_SERIALIZER_LIST = 0x85,
+ CLASSID_SERIALIZER_MAP = 0x86,
+ CLASSID_SERIALIZER_SET = 0x87,
+ CLASSID_SERIALIZER_DATE = 0x88,
+
+ CLASSID_EVENT_UNWANTMSG = 0x90,
+ CLASSID_MAILBOX = 0x91,
+ CLASSID_MAILBOXINT = 0x92,
+ CLASSID_MBOX_ELEMENT = 0x93,
+ CLASSID_PLAIN_MAILBOX = 0x94,
+ CLASSID_MBOX_MANAGER = 0x95,
+ CLASSID_PLAIN_MBOXMGR = 0x96,
+ CLASSID_DELIVERYSVC = 0x97,
+ CLASSID_TCP_DELIVERYSVC = 0x98,
+ CLASSID_CLIENTSTUB = 0x99,
+ CLASSID_SERVERSTUB = 0x9a,
+ CLASSID_SERVERFACTORY = 0x9b,
+ CLASSID_CLIENTFACTORY = 0x9c,
+
+ CLASSID_TCP_XPORTFACT = 0xb0,
+ CLASSID_SERVERFACT = 0xb1,
+ CLASSID_CLIENTFACT = 0xb2,
+ CLASSID_EXECLIENT_IMPL = 0xb3,
+ CLASSID_EXECLIENTBASE_IMPL = 0xb4,
+ CLASSID_EXESERVER_IMPL = 0xb5,
+ CLASSID_EXESERVERBASE_IMPL = 0xb6,
+
+ CLASSID_CONTROL_START = 0x101,
+ CLASSID_CONTROL_START_WAITUP = 0x102,
+ CLASSID_CONTROL_STOP = 0x103,
+ CLASSID_CONTROL_STOP_WAITDOWN = 0x104,
+ CLASSID_WAITUP = 0x105,
+ CLASSID_WAITDOWN = 0x106,
+
+ CLASSID_QUERY_IS_UP = 0x110,
+ CLASSID_QUERY_LOCALADDR = 0x111,
+ CLASSID_QUERY_REMOTEADDR = 0x112,
+ CLASSID_QUERY_WAITUP = 0x113,
+ CLASSID_QUERY_WAITDOWN = 0x114,
+
+ CLASSID_DYNAMIC_START = 0x400,
+
+} etch_classid;
+
+/*
+ * ranges of numeric types
+ */
+#define ETCHTYPE_MIN_TINY ((signed char)(0xc0)) /* -64 */
+#define ETCHTYPE_MAX_TINY ((signed char)(0x7f)) /* 127 */
+#define ETCHTYPE_MIN_BYTE ((signed char)(0x80)) /* -128 (-(2^7)) */
+#define ETCHTYPE_MAX_BYTE ((signed char)(0x7f)) /* 127 ((2^7)-1) */
+#define ETCHTYPE_MIN_INT16 ((signed short)(0x8000)) /* -65536 (-(2^15)) */
+#define ETCHTYPE_MAX_INT16 ((signed short)(0x7fff)) /* 65535 ((2^15)-1) */
+#define ETCHTYPE_MIN_INT32 ((signed int)(0x80000000)) /* (-(2^31)) */
+#define ETCHTYPE_MAX_INT32 ((signed int)(0x7fffffff)) /* ((2^31)-1) */
+#define ETCHTYPE_MIN_INT64 ((signed long long)(0x8000000000000000LL)) /* (-(2^63)) */
+#define ETCHTYPE_MAX_INT64 ((signed long long)(0x7fffffffffffffffLL)) /* ((2^63)-1) */
+#define ETCHTYPE_MIN_FLOAT (1.4e-45f)
+#define ETCHTYPE_MAX_FLOAT (3.40282346e+38f)
+#define ETCHTYPE_MIN_DOUBLE (4.9e-324)
+#define ETCHTYPE_MAX_DOUBLE (1.7976931348623157e+308)
+
+#define is_inrange_bool(n) (n == 0 || n == 1)
+#define is_inrange_tiny(n) (n >= ETCHTYPE_MIN_TINY && n <= ETCHTYPE_MAX_TINY)
+#define is_inrange_tiny_for_signed_chars(n) (n >= ETCHTYPE_MIN_TINY)
+#define is_inrange_byte(n) (n >= ETCHTYPE_MIN_BYTE && n <= ETCHTYPE_MAX_BYTE)
+#define is_inrange_int8(n) (n >= ETCHTYPE_MIN_BYTE && n <= ETCHTYPE_MAX_BYTE)
+#define is_inrange_int16(n)(n >= ETCHTYPE_MIN_INT16 && n <= ETCHTYPE_MAX_INT16)
+#define is_inrange_int32(n)(n >= ETCHTYPE_MIN_INT32 && n <= ETCHTYPE_MAX_INT32)
+#define is_inrange_int64(n)(n >= ETCHTYPE_MIN_INT64 && n <= ETCHTYPE_MAX_INT64)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHOBJTYPES_H */
diff --git a/binding-c/runtime/c/include/etch_packetizer.h b/binding-c/runtime/c/include/etch_packetizer.h
new file mode 100644
index 0000000..f7fa2b7
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_packetizer.h
@@ -0,0 +1,147 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_packetizer.h
+ * packetizes a stream data source. Reads a packet header: 32-bit flag
+ * and 32-bit length, both big-endian, verifies the flag, using length from
+ * header, reads packet data and forwards it to the packet handler.
+ * as a packet source, accepts a packet and prepends a packet header to it
+ * prior to delivering it to a data source.
+ */
+
+#ifndef ETCHPACKETIZER_H
+#define ETCHPACKETIZER_H
+
+#include "apr_thread_proc.h"
+#include "etch_session_data.h"
+#include "etch_transport_packet.h"
+#include "etch_transport_data.h"
+#include "etch_session_packet.h"
+#include "etch_arraylist.h"
+#include "etch_resources.h"
+#include "etch_flexbuffer.h"
+#include "etch_thread.h"
+#include "etch_mutex.h"
+#include "etch_url.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCHPZR_HAS_MUTEX FALSE
+
+#if(0)
+
+ PACKETIZER(TRANSPORTDATA)
+ | transportData* transport;
+ | sessionData(from, buf); // override i_sessiondata
+ | transportPacket(to, buf); // implement i_transportpacket
+ - SESSIONDATA
+ | | int sessionData (whofrom, buffer);
+ | - SESSION
+ | sessionQuery(); sessionControl(); sessionNotify();
+ - TRANSPORTPACKET
+ | int transportPacket(to, buffer);
+ | int headerSize;
+ - TRANSPORT
+ transportQuery(); transportControl(); transportNotify();
+#endif
+
+const wchar_t* ETCH_PKTIZER_MAX_PKT_SIZE_TERM;
+const int ETCH_PKTIZER_DEFMAXPKTSIZE;
+const int ETCH_PKTIZER_HEADERSIZE;
+const int ETCH_PKTIZER_SIG;
+
+struct etch_packetizer;
+
+/*
+ * etch_packetizer
+ */
+typedef struct etch_packetizer
+{
+ etch_object object;
+
+ /* transport of next lower layer of the stack (connection) */
+ i_transportdata* transport; /* not owned */
+
+ /* session of next higher layer of the stack (messagizer) */
+ i_sessionpacket* session; /* not owned */
+
+ /* - - - - - - - - - - - - - - -
+ * i_transportpacket
+ * - - - - - - - - - - - - - - -
+ */
+
+ /**
+ * i_transportpacket::transport_packet()
+ * delivers packet to the transport after adding packet header.
+ * @param to recipient
+ * @param buf flexbuffer positioned on the packet and including space for header.
+ * @return 0 success, -1 error
+ */
+ etch_transport_packet transport_packet; /* transport_packet() */
+ i_transportpacket* transportpkt; /* owned */
+ etch_transport_control transport_control; /* i_transportmessage::itransport */
+ etch_transport_notify transport_notify; /* i_transportmessage::itransport */
+ etch_transport_query transport_query; /* i_transportmessage::itransport */
+ etch_transport_get_session get_session; /* i_transportmessage::itransport */
+ etch_transport_set_session set_session; /* i_transportmessage::itransport */
+
+ /* - - - - - - - - - - - - - - -
+ * i_sessiondata
+ * - - - - - - - - - - - - - - -
+ */
+
+ /**
+ * i_sessiondata::session_data()
+ * delivers data to the session from the transport
+ * @param from from who sent the packet data
+ * @param buffer the packet from the packet source positioned at the data
+ * @return 0 success, -1 error
+ */
+ etch_session_data session_data; /* session_data() */
+ i_sessiondata* sessiondata; /* owned */
+ etch_session_control session_control; /* i_sessionpacket::isession */
+ etch_session_notify session_notify; /* i_sessionpacket::isession */
+ etch_session_query session_query; /* i_sessionpacket::isession */
+
+ int (*process_header) (struct etch_packetizer*, etch_flexbuffer*, const int is_reset);
+
+ etch_mutex_t* datalock;
+
+ etch_flexbuffer* savebuf; /* owned */
+
+ size_t headersize;
+ size_t bodylength;
+ size_t maxpacketsize;
+ unsigned char is_wantheader; /* logic state flag: set true in ctor */
+
+} etch_packetizer;
+
+etch_packetizer* new_packetizer (i_transportdata*, wchar_t* uri, etch_resources*);
+etch_packetizer* new_packetizer_a(i_transportdata*, etch_url*, etch_resources*);
+
+i_session* etch_pktizer_get_session(void*);
+int etch_pktizer_process_header (etch_packetizer*, etch_flexbuffer*, const int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHPACKETIZER_H */
diff --git a/binding-c/runtime/c/include/etch_plain_mailbox.h b/binding-c/runtime/c/include/etch_plain_mailbox.h
new file mode 100644
index 0000000..be48833
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_plain_mailbox.h
@@ -0,0 +1,158 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_plainmailbox.h
+ * standard mailbox using a fixed size queue
+ */
+
+#ifndef ETCHPLAINMBOX_H
+#define ETCHPLAINMBOX_H
+
+#include "etch_mailbox.h"
+#include "etch_mailbox_manager.h"
+#include "etch_simpletimer.h"
+#include "etch_thread.h"
+#include "etch_queue.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MBOX_LIFETIME_UNTIL_CLOSE 0
+#define MBOX_DEFMAXMESSAGES 8
+#define ETCH_MAILBOX_RESULT_ALREADY_CLOSED 1
+
+#define ETCH_MAILBOX_STATE_INITIAL 0
+#define ETCH_MAILBOX_STATE_OPEN 2
+#define ETCH_MAILBOX_STATE_CLOSED_DELIVERY 4
+#define ETCH_MAILBOX_STATE_CLOSED_READ 6
+#define ETCH_MAILBOX_STATE_SHUTDOWN 8
+
+typedef etch_timer_callback etch_alarm_wakeup;
+
+#if(0)
+
+ PLAINMAILBOX(MAILBOXMANAGER)
+ |- ALARMLISTENER
+ | etch_alarm_wakeup()
+ - MAILBOX
+ | etch_mailbox_message()
+ | etch_mailbox_read()
+ | etch_mailbox_read_withwait()
+ | etch_mailbox_close_delivery()
+ | etch_mailbox_close_read()
+ | etch_mailbox_register_notify()
+ | etch_mailbox_unregister_notify()
+ | etch_mailbox_is_empty()
+ | etch_mailbox_is_closed()
+ | etch_mailbox_is_full()
+ | etch_mailbox_get_message_id()
+ -
+#endif
+
+struct i_mailbox_manager;
+
+/*
+ * etch_plainmailbox
+ * this object is instantiated, and is also a mask over any other mailbox
+ * class object which may be defined. etch_plainmailbox is therefore
+ * typedef'ed below as etch_mailbox.
+ */
+typedef struct etch_plainmailbox
+{
+ etch_object object;
+
+ void* impl;
+ struct i_mailbox_manager* manager;
+
+ /* - - - - - - - - - - - - - - -
+ * notify interface
+ * - - - - - - - - - - - - - - -
+ */
+ etch_mailbox_notify notify;
+
+ /* - - - - - - - - - - - - - - -
+ * alarm listener interface
+ * - - - - - - - - - - - - - - -
+ */
+ etch_alarm_wakeup wakeup;
+
+ /* - - - - - - - - - - - - - - -
+ * i_mailbox interface
+ * - - - - - - - - - - - - - - -
+ */
+ i_mailbox* imailbox;
+
+ etch_mailbox_message message;
+ etch_mailbox_read read;
+ etch_mailbox_read_withwait read_withwait;
+ etch_mailbox_close_delivery close_delivery;
+ etch_mailbox_close_read close_read;
+ etch_mailbox_register_notify register_notify;
+ etch_mailbox_unregister_notify unregister_notify;
+ etch_mailbox_is_empty is_empty;
+ etch_mailbox_is_closed is_closed;
+ etch_mailbox_is_full is_full;
+ etch_mailbox_get_message_id get_message_id;
+
+ /* - - - - - - - - - - - - - - -
+ * etch_mailbox instance data
+ * - - - - - - - - - - - - - - -
+ */
+ int lifetime;
+ int max_messages;
+ int max_message_delay;
+ int64 message_id;
+ etch_queue* queue;
+ etch_timer* lifetimer;
+ etch_mutex* rwlock; /* global read write lock - not owned */
+ etch_mutex* readlock; /* unused - lose it */
+ etch_object* notify_state;
+
+ unsigned char is_alarm_set;
+ unsigned char mailbox_state;
+ unsigned char unused_a;
+ unsigned char unused_b;
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * derivations must include all the above and define data following
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+} etch_plainmailbox;
+
+etch_mailbox* new_mailbox
+ ( struct i_mailbox_manager*,
+ const int64 message_id,
+ const int max_msgdelay,
+ const int lifetime,
+ const int max_messages
+ );
+
+int etchmbox_contains_message(etch_mailbox*, etch_message*);
+int etchmbox_get_readlock (etch_plainmailbox*, const char*);
+int etchmbox_release_readlock (etch_plainmailbox*, const char*);
+int etchmbox_get_readlockex (etch_mutex*, const char*);
+int etchmbox_release_readlockex (etch_mutex*, const char*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHPLAINMBOX_H */
diff --git a/binding-c/runtime/c/include/etch_plain_mailbox_manager.h b/binding-c/runtime/c/include/etch_plain_mailbox_manager.h
new file mode 100644
index 0000000..6fad8ed
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_plain_mailbox_manager.h
@@ -0,0 +1,134 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_plainmailboxmgr.h
+ * mailbox manager accepts packets for potential delivery to a mailbox or to an
+ * alternate message handler if an appropriate mailbox is not available.
+ * a mailbox can be created on request keyed by the a message's ID.
+ */
+
+#ifndef ETCHPLAINMBOXMGR_H
+#define ETCHPLAINMBOXMGR_H
+
+#include "etch_mailbox_manager.h"
+#include "etch_resources.h"
+#include "etch_mutex.h"
+#include "etch_url.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCHMBMGR_DEFNUMMAILBOXES 8
+
+#if(0)
+
+ PLAINMAILBOXMGR
+ | register(mailbox)
+ | unregister(mailbox)
+ | unregisterAll();
+ | getMailbox(messageID);
+ - MAILBOXMANAGER
+ | transportCall(Who to, message)
+ | redeliver(Who from, message)
+ | unregister(mailbox)
+ |- SESSIONMESSAGE<SessionData>
+ | | sessionMessage(Who from, message)
+ | - SESSION
+ | sessionQuery(); sessionControl(); sessionNotify();
+ - TRANSPORTMESSAGE
+ | transportMessage(to, Message);
+ - TRANSPORT
+ transportQuery(); transportControl(); transportNotify();
+ getSession(); setSession();
+#endif
+
+/*
+ * etch_plainmailboxmgr
+ */
+typedef struct etch_plainmailboxmgr
+{
+ etch_object object;
+
+ int max_delay;
+ etch_mutex* xlock; /* owned - unused */
+ etch_mutex* rwlock; /* not owned */
+ etch_hashtable* mailboxes; /* owned */
+ unsigned char is_connection_up;
+
+ /* - - - - - - - - - - - - - - -
+ * i_mailbox_manager interface
+ * - - - - - - - - - - - - - - -
+ */
+ i_mailbox_manager* imanager; /* owned */
+ etch_mbm_transport_call transport_call;
+ etch_mbm_redeliver redeliver;
+ etch_mbm_unregister unregister;
+
+ /* i_transportmessage of next lower layer (messagizer) */
+ i_transportmessage* transport; /* not owned */
+
+ /* i_sessionmessage of next higher layer (delivery service) */
+ i_sessionmessage* session; /* not owned */
+
+ /* - - - - - - - - - - - - - - -
+ * i_sessionmessage interface
+ * - - - - - - - - - - - - - - -
+ */
+ i_sessionmessage* isessionmsg; /* owned */
+ etch_session_message session_message;
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+ /* - - - - - - - - - - - - - - -
+ * i_transportmessage interface
+ * - - - - - - - - - - - - - - -
+ */
+ i_transportmessage* transportmsg; /* owned */
+ etch_transport_message transport_message;
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+} etch_plainmailboxmgr;
+
+/**
+ * new_plain_mailbox_manager()
+ * etch_plainmailboxmgr constructor
+ * @param itm i_transportmessage interface object, caller retains ownership.
+ * @param uri url string, caller relinquishes ownership.
+ * @param resxmap caller retains ownership.
+ */
+etch_plainmailboxmgr* new_plain_mailbox_manager(i_transportmessage*, wchar_t* uri, etch_resources*, etch_mutex*);
+
+i_mailbox* pmboxmgr_get_mailbox(etch_plainmailboxmgr*, etch_int64* msgid);
+
+int pmboxmgr_register_mailbox(etch_plainmailboxmgr*, i_mailbox*);
+int pmboxmgr_unregister_all(etch_plainmailboxmgr*);
+etch_hashtable* new_pmboxmgr_mailboxmap();
+int pmboxmgr_size(etch_plainmailboxmgr*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHPLAINMBOXMGR_H */
diff --git a/binding-c/runtime/c/include/etch_queue.h b/binding-c/runtime/c/include/etch_queue.h
new file mode 100644
index 0000000..fb8be61
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_queue.h
@@ -0,0 +1,93 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_queue.h
+ * synchronized FIFO queue
+ */
+#ifndef ETCHQUEUE_H
+#define ETCHQUEUE_H
+
+#include "etch_object.h"
+#include "etch_thread.h"
+#include "etch_queue_apr.h"
+#include "etch_collection.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_DEFQSIZE 32
+
+#define ETCHQUEUE_CONTENT_SIMPLE 0 /* content memory freed as a unit */
+#define ETCHQUEUE_CONTENT_OBJECT 1 /* content is etchobject */
+
+#define ETCH_QUEUE_OPERATION_TIMEOUT 1
+#define ETCH_QUEUE_OPERATION_CANCELED 2
+#define ETCH_QUEUE_EOF 3
+
+#define ETCHQUEUE_NEEDLOCK TRUE
+#define ETCHQUEUE_NOLOCK FALSE
+
+typedef int (*queuecallback) (int, void*); /* queue callback signature */
+
+/**
+ * etch_queue
+ */
+typedef struct etch_queue
+{
+ etch_object object;
+
+ etch_apr_queue_t* aprq;
+ unsigned short content_obj_type; /* etch obj_type of a native value */
+ unsigned short content_class_id; /* etch class_id of a native value */
+ unsigned int qcapacity;
+
+ /* this object may be masked by etch_collection_mask to determine content
+ * type and class, so do not add any fields above this comment */
+
+ i_iterable iterable;
+ apr_pool_t* subpool;
+ queuecallback freehook;
+ unsigned char is_readonly;
+ unsigned char content_type;
+
+} etch_queue;
+
+
+etch_queue* new_queue(const int initialsize);
+int etchqueue_put(etch_queue*, void* item);
+int etchqueue_try_put(etch_queue*, void* item);
+int etchqueue_put_withwait(etch_queue*, const int waitms, void* item);
+int etchqueue_get(etch_queue*, void** itemout);
+int etchqueue_try_get(etch_queue*, void** itemout);
+int etchqueue_get_withwait(etch_queue*, const int waitms, void** itemout);
+int etchqueue_notify_all(etch_queue*);
+int etchqueue_lock(etch_queue*);
+int etchqueue_unlock(etch_queue*);
+int etchqueue_trylock(etch_queue*);
+int etchqueue_size(etch_queue*);
+int etchqueue_is_closed(etch_queue*);
+int etchqueue_is_full(etch_queue*);
+int etchqueue_close(etch_queue*, const int is_needlock);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHQUEUE_H */
diff --git a/binding-c/runtime/c/include/etch_queue_apr.h b/binding-c/runtime/c/include/etch_queue_apr.h
new file mode 100644
index 0000000..f523feb
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_queue_apr.h
@@ -0,0 +1,89 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_queue_apr.h
+ * based on apr_queue, with timeouts added on push and pop waits.
+ */
+
+#ifndef ETCHQUEUEAPR_H
+#define ETCHQUEUEAPR_H
+
+#define ETCHQUEUE_CLEARING_CLOSED_QUEUE (-2)
+
+#include "etch_object.h"
+#include "apr_thread_cond.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * etch_apr_queue_t
+ * same as the private apr_queue_t
+ */
+typedef struct etch_apr_queue_t
+{
+ void **data;
+ unsigned int nelts; /**< # elements */
+ unsigned int in; /**< next empty location */
+ unsigned int out; /**< next filled location */
+ unsigned int bounds;/**< max size of queue */
+ unsigned int full_waiters;
+ unsigned int empty_waiters;
+ apr_thread_mutex_t *one_big_mutex;
+ apr_thread_cond_t *not_empty;
+ apr_thread_cond_t *not_full;
+ int terminated;
+
+} etch_apr_queue_t;
+
+
+apr_status_t etch_apr_queue_create(etch_apr_queue_t **queue,
+ unsigned int queue_capacity, apr_pool_t *a);
+
+apr_status_t etch_apr_queue_push(etch_apr_queue_t *queue,
+ apr_interval_time_t timeout, void *data);
+
+apr_status_t etch_apr_queue_pop(etch_apr_queue_t *queue,
+ apr_interval_time_t timeout, void **data);
+
+apr_status_t etch_apr_queue_trypush(etch_apr_queue_t *queue, void *data);
+
+apr_status_t etch_apr_queue_trypop(etch_apr_queue_t *queue, void **data);
+
+unsigned int etch_apr_queue_size(etch_apr_queue_t*);
+
+apr_status_t etch_apr_queue_interrupt_all(etch_apr_queue_t*);
+
+apr_status_t etch_apr_queue_unsafe_interrupt_all(etch_apr_queue_t*);
+
+apr_status_t etch_apr_queue_term(etch_apr_queue_t *queue);
+
+apr_status_t etch_apr_queue_unsafeclose(etch_apr_queue_t*);
+
+apr_status_t etch_apr_queue_trylock(etch_apr_queue_t*);
+
+apr_status_t etch_apr_queue_lock(etch_apr_queue_t*);
+
+apr_status_t etch_apr_queue_unlock(etch_apr_queue_t*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHQUEUEAPR_H */
diff --git a/binding-c/runtime/c/include/etch_remote.h b/binding-c/runtime/c/include/etch_remote.h
new file mode 100644
index 0000000..8a917fc
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_remote.h
@@ -0,0 +1,64 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_remote.h
+ * remote base
+ */
+
+#ifndef ETCH_REMOTE_H
+#define ETCH_REMOTE_H
+
+#include "etch_object.h"
+#include "etch_thread.h"
+#include "etch_transport.h"
+#include "etch_session_message.h"
+#include "etch_svcobj_masks.h"
+#include "etch_message.h"
+#include "etch_default_value_factory.h"
+
+#define ETCH_REMOTETYPE_NONE 0
+#define ETCH_REMOTETYPE_CLIENT 1
+#define ETCH_REMOTETYPE_SERVER 2
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+xxxx_remote* new_etch_remote_base (void* thisx, const int objsize,
+ const unsigned short class_id, i_delivery_service*,
+ etch_value_factory*, etch_object*);
+
+int etchremote_start_waitup (void* data, const int waitms);
+int etchremote_stop_waitdown (void* data, const int waitms);
+int etchremote_transport_control (void* data, etch_event* evt, etch_object* value); /* value could be an etch_int32 */
+int etchremote_transport_notify (void* data, etch_event* evt);
+etch_object* etchremote_transport_query (void* data, etch_query* query);
+etch_message* etchremote_new_message (void* data, etch_type* message_type);
+int etchremote_send (void* data, etch_message* msg);
+void* etchremote_sendex (void* data, etch_message* msg);
+int etchremote_begincall (void* data, etch_message* msg, void** out);
+int etchremote_endcall (void* data, i_mailbox* mbox, etch_type* response_type, void** out);
+
+int is_etch_remote(void* x);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_REMOTE_H */
diff --git a/binding-c/runtime/c/include/etch_resources.h b/binding-c/runtime/c/include/etch_resources.h
new file mode 100644
index 0000000..86e6632
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_resources.h
@@ -0,0 +1,64 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_resources.h
+ * resource map
+ * a string-to-etch-object-based hashtable
+ */
+
+#ifndef ETCHRESOURCES_H
+#define ETCHRESOURCES_H
+
+#include "etch_hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define ETCH_RESOURCES_DEFSIZE 16
+
+extern const wchar_t* ETCH_RESXKEY_SOCKET;
+extern const wchar_t* ETCH_RESXKEY_ACCEPTED_CONX;
+extern const wchar_t* ETCH_RESXKEY_POOLTYPE_FREE;
+extern const wchar_t* ETCH_RESXKEY_POOLTYPE_QUEUED;
+extern const wchar_t* ETCH_RESXKEY_MSGIZER_FORMAT;
+extern const wchar_t* ETCH_RESXKEY_MSGIZER_VALUFACT;
+extern const wchar_t* ETCH_RESXVAL_XPORTFMT_BINARY;
+extern const wchar_t* ETCH_RESXVAL_XPORTFMT_XML;
+extern const wchar_t* ETCH_XFACTKEY_TCP;
+extern const wchar_t* ETCH_XPORTKEY_START;
+extern const wchar_t* ETCH_XPORTKEY_START_AND_WAIT_UP;
+extern const wchar_t* ETCH_XPORTKEY_IS_UP;
+extern const wchar_t* ETCH_XPORTKEY_STOP;
+extern const wchar_t* ETCH_XPORTKEY_STOP_AND_WAIT_DOWN;
+
+
+typedef etch_hashtable etch_resources;
+etch_resources* new_etch_resources (const int initialsize);
+etch_object* etch_resources_get (etch_resources*, const wchar_t* key);
+int etch_resources_add (etch_resources*, const wchar_t* key, etch_object* value);
+int etch_resources_replace (etch_resources*, const wchar_t* key, etch_object* value);
+int etch_resources_clear (etch_resources*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHRESOURCES_H */
diff --git a/binding-c/runtime/c/include/etch_runtime.h b/binding-c/runtime/c/include/etch_runtime.h
new file mode 100644
index 0000000..8aa8a0b
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_runtime.h
@@ -0,0 +1,92 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_runtime.h -- runtime methods.
+ */
+
+#ifndef _ETCH_RUNTIME_H_
+#define _ETCH_RUNTIME_H_
+
+#include "etch.h"
+#include "etch_config.h"
+#include "etch_log.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * malloc'ed global constants
+ */
+typedef struct etch_global_constants
+{
+ wchar_t* etch_charsetname_us_ascii;
+ wchar_t* etch_charsetname_utf8;
+ wchar_t* etch_charsetname_utf16;
+
+ char* pctd; /* for "%d" sprintf mask */
+
+} etch_global_constants;
+
+
+// shutdown hook function
+typedef etch_status_t (*etch_runtime_shutdown_hook_func)();
+
+/**
+ * etch runtime mem abort function
+ */
+int etch_runtime_mem_abort(int retcode);
+
+/**
+ * initialize the etch runtime components.
+ */
+etch_status_t etch_runtime_initialize(etch_config_t* config);
+
+/**
+ * gets the runtime version.
+ */
+etch_status_t etch_runtime_get_version(uint16* major, uint16* minor, uint16* revision);
+
+/**
+ * gets the runtime configuration.
+ */
+etch_status_t etch_runtime_get_config(etch_config_t** config);
+
+/**
+ * gets the runtime logger.
+ */
+etch_status_t etch_runtime_get_logger(etch_log_t** logger);
+
+/**
+ * register callback function, witch will be called when
+ * the runtime shutdown.
+ */
+etch_status_t etch_runtime_shutdown_hook_add(etch_runtime_shutdown_hook_func);
+
+/**
+ * shutdown the etch runtime components
+ */
+etch_status_t etch_runtime_shutdown();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_RUNTIME_H */
diff --git a/binding-c/runtime/c/include/etch_serializer.h b/binding-c/runtime/c/include/etch_serializer.h
new file mode 100644
index 0000000..ce9ad25
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_serializer.h
@@ -0,0 +1,82 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_serializer.h
+ * builtin custom type serializers
+ */
+
+#ifndef ETCH_SERIALIZER_H
+#define ETCH_SERIALIZER_H
+
+#include "etch_object.h"
+#include "etch_id_name.h"
+#include "etch_type.h"
+#include "etch_field.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* we restate typedefs stated elsewhere, here,
+ * since we can't include those headers in this one.
+ */
+struct etch_hashtable;
+struct etch_validator;
+struct etch_serializer;
+typedef struct etch_serializer* (*etch_serializer_ctor) (etch_type*, etch_field*);
+
+
+/**
+ * etch_serializer
+ * import/export helper
+ */
+typedef struct etch_serializer
+{
+ etch_object object;
+
+ etch_object* impl;
+
+ etch_object* (*export_value) (struct etch_serializer* thisx, etch_object* objval);
+
+ etch_object* (*import_value) (struct etch_serializer* thisx, etch_object* structval);
+
+ etch_id_name* type; /* not owned */
+ etch_id_name* field; /* not owned */
+
+} etch_serializer;
+
+etch_serializer* new_etch_serializer(const int objsize);
+
+int etch_serializer_init(etch_type*, const wchar_t*, const unsigned,
+ struct etch_hashtable*, struct etch_validator*, etch_serializer_ctor);
+
+int etch_serializer_exception_init(etch_type*, struct etch_hashtable* c2tmap);
+int etch_serializer_rtxcp_init (etch_type*, struct etch_hashtable* c2tmap);
+int etch_serializer_authxcp_init (etch_type*, struct etch_hashtable* c2tmap);
+int etch_serializer_list_init (etch_type*, struct etch_hashtable* c2tmap);
+int etch_serializer_map_init (etch_type*, struct etch_hashtable* c2tmap);
+int etch_serializer_set_init (etch_type*, struct etch_hashtable* c2tmap);
+int etch_serializer_date_init (etch_type*, struct etch_hashtable* c2tmap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_SERIALIZER_H */
diff --git a/binding-c/runtime/c/include/etch_session_data.h b/binding-c/runtime/c/include/etch_session_data.h
new file mode 100644
index 0000000..0c30528
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_session_data.h
@@ -0,0 +1,83 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sessiondata.h
+ * i_sessiondata interface
+ * interface used to deliver packets to the session from the transport
+ */
+#ifndef ETCHISESSIONDATA_H
+#define ETCHISESSIONDATA_H
+
+#if(0)
+
+ SESSIONDATA
+ | int sessionData(Who from, buffer)
+ - SESSION
+ sessionQuery(); sessionControl(); sessionNotify();
+
+#endif
+
+
+#include "etch_sessionint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*etch_session_data) (void* thisx, void* whofrom, void* buffer);
+
+
+/**
+ * i_sessiondata
+ * sessiondata interface
+ * a message handler delivers messages from a message source
+ */
+typedef struct i_sessiondata
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+
+ /* session interface */
+ i_session* isession;
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+ /**
+ * session_data()
+ * delivers packets to the session from the transport
+ * @param from whofrom who sent the message.
+ * caller relinquishes this object on success, retains on failure.
+ * @param buf the packet buffer from the packet source.
+ * caller relinquishes this object on success, retains on failure.
+ * @return 0 success, -1 error
+ */
+ etch_session_data session_data;
+
+} i_sessiondata;
+
+
+i_sessiondata* new_sessiondata_interface(void* thisx, etch_session_data, i_session*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHISESSIONDATA_H */
diff --git a/binding-c/runtime/c/include/etch_session_listener.h b/binding-c/runtime/c/include/etch_session_listener.h
new file mode 100644
index 0000000..87948d2
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_session_listener.h
@@ -0,0 +1,132 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sessionmsg.h
+ * i_sessionmessage interface
+ * interface used to deliver messages to the session from the transport
+ */
+#ifndef ETCHISESSIONLISTEN_H
+#define ETCHISESSIONLISTEN_H
+
+#if(0)
+
+ SESSIONLISTENER
+ | int sessionAccepted(socket)
+ - SESSION
+ sessionQuery(); sessionControl(); sessionNotify();
+
+#endif
+
+#include "etch_sessionint.h"
+#include "etch_transportint.h"
+#include "etch_resources.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* session accepted interface */ // 11/16
+typedef int (*etch_session_accepted) (void* thisx, void* acceptedconx);
+
+/* this is the signature that must be called from accept */
+/* this function is defined in servermain */
+typedef void* (*funcptr_new_impl_server) (void* thisx, void* remote_client);
+struct i_sessionlistener;
+
+typedef int (*etchlistener_waitexit) (struct i_sessionlistener*);
+
+/**
+ * i_server_factory
+ * server factory interface
+ */
+typedef struct i_server_factory
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+ funcptr_new_impl_server new_server; /* server constructor */
+
+ etch_object* session; /* not owned */
+ i_session* isession; /* not owned */
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+} i_server_factory;
+
+
+/**
+ * i_sessionlistener
+ * sessionlistener interface
+ */
+typedef struct i_sessionlistener
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+
+ /* session interface */
+ void* session; /* not owned */
+ i_session* isession; /* may be owned or not */
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+ /**
+ * session_accepted()
+ * delivers a socket to the session from the listener
+ * @param socket caller retains
+ * @return 0 success, -1 failure
+ */
+ etch_session_accepted session_accepted;
+
+ /* transport interface */
+ void* transport; /* not owned */
+ i_transport* itransport; /* may be owned or not */
+ /* these virtuals are proxied up to this level since [main]
+ * does not see the specific transport header e.g. tcpserver */
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_set_session set_session;
+ etch_transport_get_session get_session;
+
+ void* url; /* owned */
+ void* server_params; /* owned */
+ etch_resources* resources; /* owned */
+ etchlistener_waitexit wait_exit; /* listener thread exit wait */
+
+ unsigned char is_session_owned;
+ unsigned char is_transport_owned;
+ unsigned char is_resources_owned;
+ unsigned char unused;
+
+} i_sessionlistener;
+
+
+i_sessionlistener* new_sessionlistener_interface (void* thisx, etch_session_accepted, i_session*);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHISESSIONLISTEN_H */
diff --git a/binding-c/runtime/c/include/etch_session_message.h b/binding-c/runtime/c/include/etch_session_message.h
new file mode 100644
index 0000000..b8e6f56
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_session_message.h
@@ -0,0 +1,84 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sessionmsg.h
+ * i_sessionmessage interface
+ * interface used to deliver messages to the session from the transport
+ */
+#ifndef ETCHISESSIONMSG_H
+#define ETCHISESSIONMSG_H
+
+#if(0)
+
+ SESSIONMESSAGE
+ | int sessionMessage(Who from, message)
+ - SESSION
+ sessionQuery(); sessionControl(); sessionNotify();
+
+#endif
+
+
+#include "etch_sessionint.h"
+#include "etch_object.h"
+#include "etch_message.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*etch_session_message) (void* , etch_who* , etch_message* );
+
+
+/**
+ * i_sessionmessage
+ * sessionmessage interface
+ * a message handler delivers messages from a message source
+ */
+typedef struct i_sessionmessage
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+
+ /* session interface */
+ i_session* isession;
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+ /**
+ * session_message()
+ * delivers data to the session from the transport
+ * @param from whofrom who sent the message.
+ * caller relinquishes this object on success, retains on failure.
+ * @param message the message from the message source.
+ * caller relinquishes this object on success, retains on failure.
+ * @return 0 success, -1 error
+ */
+ etch_session_message session_message;
+
+} i_sessionmessage;
+
+i_sessionmessage* new_sessionmsg_interface(void* thisx, etch_session_message sm, i_session* session);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHISESSIONMSG_H */
diff --git a/binding-c/runtime/c/include/etch_session_packet.h b/binding-c/runtime/c/include/etch_session_packet.h
new file mode 100644
index 0000000..8b3bb77
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_session_packet.h
@@ -0,0 +1,81 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sessionpkt.h
+ * i_sessionpacket interface
+ * interface used to deliver packets to the session from the transport
+ */
+#ifndef ETCHISESSIONPACKET_H
+#define ETCHISESSIONPACKET_H
+
+#if(0)
+
+ SESSIONPACKET
+ | int sessionPacket(Who from, packet)
+ - SESSION
+ sessionQuery(); sessionControl(); sessionNotify();
+
+#endif
+
+
+#include "etch_sessionint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*etch_session_packet) (void* thisx, void* whofrom, void* buffer);
+
+
+/**
+ * i_sessionpacket
+ * sessionpacket interface
+ */
+typedef struct i_sessionpacket
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+
+ /* session interface */
+ i_session* isession;
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+ /**
+ * session_packet()
+ * delivers data to the session from the transport.
+ * @param from whofrom who sent the packet
+ * @param packet the packet from the packet source
+ * @return 0 OK, -1 buffer contents invalid.
+ */
+ etch_session_packet session_packet;
+
+
+} i_sessionpacket;
+
+
+i_sessionpacket* new_sessionpkt_interface(void* thisx, etch_session_packet, i_session*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHISESSIONPACKET_H */
diff --git a/binding-c/runtime/c/include/etch_sessionint.h b/binding-c/runtime/c/include/etch_sessionint.h
new file mode 100644
index 0000000..311f7b7
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_sessionint.h
@@ -0,0 +1,129 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sessionint.h
+ * session interface
+ */
+#ifndef ETCHISESSION_H
+#define ETCHISESSION_H
+
+#include "etch_object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char* ETCHDSIF;
+#define ETCHEVT_SESSION_UP 0x80
+#define ETCHEVT_SESSION_DOWN 0x81
+
+/*
+ * the memory ownership contracts for control, notify, and query, are:
+ * caller relinquishes all arguments except of course the this pointer,
+ * regardless of outcome. it follows that only simple and temporal
+ * objects are passed as parameters.
+ */
+typedef int (*etch_session_control)(void* thisx, etch_event* evt, etch_object* value);
+typedef int (*etch_session_notify) (void* thisx, etch_event* evt);
+typedef etch_object* (*etch_session_query) (void* thisx, etch_query* query);
+
+/**
+ * i_session
+ * session interface
+ * not an etch object, ergo no ->desroy(), no ->clone()
+ */
+typedef struct i_session
+{
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+ void* thisx;
+
+} i_session;
+
+/**
+ * i_session_mask
+ * mask over any implemented session interface
+ */
+typedef struct i_session_mask
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+
+ /* session interface */
+ i_session* isession;
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+ void* main_session_method;
+
+} i_session_mask;
+
+i_session* new_default_session_interface(void* thisx);
+i_session* new_session_interface(void* thisx, etch_session_control sc, etch_session_notify sn, etch_session_query sq);
+i_session* clone_session(void* thisx, const i_session*);
+
+/**
+ * i_objsession
+ * obj_session interface
+ * same interface, names changed to avoid collisions.
+ * not determined whether this is necessary in the c binding.
+ * not an etch object, ergo no ->destroy(), no ->clone()
+ */
+typedef struct i_objsession
+{
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ void* thisx;
+
+} i_objsession;
+
+/**
+ * etch_objsession_objinfo
+ * session methods parameter info struct
+ */
+typedef struct etch_objsession_objinfo
+{
+ etch_object* obj;
+ etch_object* resobj;
+ etch_object* msg;
+ etch_who* whofrom;
+ char* msg_aname;
+ etch_exception* exception;
+ unsigned short obj_type;
+ unsigned short class_id;
+ unsigned char is_message;
+ unsigned char is_exception;
+
+} etch_objsession_objinfo;
+
+i_objsession* new_default_objsession_interface(void* thisx);
+i_objsession* new_objsession_interface(void* thisx, etch_session_control sc, etch_session_notify sn, etch_session_query sq);
+void etchsession_get_objinfo(etch_objsession_objinfo* objinfo, void* evt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHISESSION_H */
diff --git a/binding-c/runtime/c/include/etch_simpletimer.h b/binding-c/runtime/c/include/etch_simpletimer.h
new file mode 100644
index 0000000..6dd627a
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_simpletimer.h
@@ -0,0 +1,84 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_simpletimer.h
+ * thread-per-use one-shot relative timer
+ * this should be replaced with a timer pool implementation when development resources permit
+ */
+
+#ifndef ETCH_SIMPLETIMER_H
+#define ETCH_SIMPLETIMER_H
+
+#include "apr_thread_proc.h" /* must be included first */
+#include "etch_thread.h" /* must be included second */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_TIMER_REASON_TIMEOUT ETCH_TIMEOUT
+#define ETCH_TIMER_REASON_INTERRUPTED ETCH_INTERRUPTED
+#define ETCH_TIMER_REASON_ERROR (-1)
+
+
+typedef etch_thread etch_timer;
+
+
+/**
+ * etch_timer_callback
+ * signature of timer expiration callback.
+ * @param passthrudata user object passed to timer constructor.
+ * @param reason expiration reason 1 timeout, 0 signaled, -1 error.
+ */
+typedef void (*etch_timer_callback) (void* passthrudata, const int reason);
+
+
+/**
+ * new_timer()
+ * etch_timer constructor
+ * @param durationms timer duration in milliseconds.
+ * @param passthrudata user data to be returned in expiration callback.
+ * caller retains ownership of this memory.
+ * @param callback expiration callback void (*callback) (void*, int);
+ * in which creator's passthrudata is passed as the first parameter, and the timeout
+ * reason passed in parameter 2 (reason value 0 is signaled, 1 is timeout, -1 error).
+ * @return the timer object. caller owns this object but must not destroy it until
+ * after the expiration callback has completed and returned.
+ */
+etch_timer* new_timer(const int durationms, void* passthrudata, etch_timer_callback);
+
+
+/**
+ * etch_timer_start()
+ * start the specified timer. a timer must always be explicitly started.
+ */
+int etch_timer_start(etch_timer*);
+
+
+/**
+ * etch_timer_stop()
+ * signal specified timer to stop waiting and exit its thread.
+ */
+int etch_timer_stop (etch_timer*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_SIMPLETIMER_H */
diff --git a/binding-c/runtime/c/include/etch_sourceint.h b/binding-c/runtime/c/include/etch_sourceint.h
new file mode 100644
index 0000000..2b0f2ea
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_sourceint.h
@@ -0,0 +1,75 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sourceint.h
+ * source interface
+ */
+#ifndef ETCHISOURCE_H
+#define ETCHISOURCE_H
+
+#include "etch_transportint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* (*etch_source_get_handler) (void* thisx);
+typedef void (*etch_source_set_handler) (void* thisx, void* handler);
+
+
+/**
+ * i_source
+ * source interface
+ * common interface to sources of data, packets, or messages.
+ * models the various handlers' view of a communication channel
+ */
+typedef struct i_source
+{
+ etch_object object;
+
+ void* thisx; /* object which is the i_source */
+
+ /**
+ * get_handler(this) returns an object implementing i_sourcehandler
+ */
+ etch_source_get_handler get_handler;
+
+ /**
+ * set_handler(this, handler) expects an object implementing i_sourcehandler
+ */
+ etch_source_set_handler set_handler;
+
+ /**
+ * transport interface (query, control, notify), not an etch object
+ */
+ i_transport* itransport;
+
+} i_source;
+
+
+i_source* new_default_source_interface();
+
+i_source* new_source_interface(void* thisx,
+ etch_source_get_handler gh, etch_source_set_handler sh, i_transport* xp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHISOURCE_H */
diff --git a/binding-c/runtime/c/include/etch_structval.h b/binding-c/runtime/c/include/etch_structval.h
new file mode 100644
index 0000000..9d54de6
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_structval.h
@@ -0,0 +1,64 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_structval.h -- defines the etch_structvalue object.
+ * etch_structvalue is a typed map of key/value pairs, where type is an etch_type,
+ * a key is an etch_field, and value is an etch object. all values are the same
+ * class, corresponding to the struct type.
+ */
+
+#ifndef ETCHSTRUCTVAL_H
+#define ETCHSTRUCTVAL_H
+
+#include "etch_msgutl.h"
+#include "etch_tagdata_inp.h"
+#include "etch_tagdata_out.h"
+#include "etch_hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * etch_structvalue
+ */
+typedef struct etch_structvalue
+{
+ etch_object object;
+
+ etch_hashtable* items; /* owned */
+ etch_type* struct_type; /* not owned */
+
+} etch_structvalue;
+
+
+etch_structvalue* new_structvalue(etch_type*, const int initialsize);
+int structvalue_put (etch_structvalue*, etch_field* key, etch_object* value);
+int structvalue_is_type (etch_structvalue*, etch_type*);
+etch_object* structvalue_get(etch_structvalue*, etch_field* key);
+etch_object* structvalue_remove(etch_structvalue*, etch_field* key);
+etch_hashtable* new_structvalue_hashtable();
+int structvalue_count(etch_structvalue*);
+int destroy_structvalue(void* data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHSTRUCTVAL_H */
diff --git a/binding-c/runtime/c/include/etch_stub.h b/binding-c/runtime/c/include/etch_stub.h
new file mode 100644
index 0000000..5315b89
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_stub.h
@@ -0,0 +1,130 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_stub.h
+ * a translation of the java binding StubBase
+ */
+
+#ifndef ETCH_STUB_H
+#define ETCH_STUB_H
+
+#include "etch_object.h"
+#include "etch_thread.h"
+#include "etch_transport.h"
+#include "etch_session_message.h"
+#include "etch_message.h"
+#include "etch_default_value_factory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_STUBTYPE_NONE 0
+#define ETCH_STUBTYPE_CLIENT 1
+#define ETCH_STUBTYPE_SERVER 2
+
+/*
+ * etch_stub
+ * stub base quasi interface
+ */
+typedef struct etch_stub
+{
+ etch_object object;
+
+ etch_object* obj; /* concrete client or server object, not owned */
+ etch_object* stubobj; /* stub xxxx_either_stub* container, not owned */
+ i_objsession* impl_callbacks; /* impl's session callbacks, not owned */
+ i_delivery_service* delivery_service; /* not owned */
+
+ /* - - - - - - - - - -
+ * i_sessionmessage
+ * - - - - - - - - - -
+ */
+ i_session* isession; /* owned by isessionmsg */
+ i_sessionmessage* isessionmsg; /* owned */
+ etch_session_message session_message;
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+ /* TODO lose threadpools and delivery params, they are here */
+ etch_server_factory* params; /* not owned */
+
+ unsigned char stub_type;
+ unsigned char is_implobj_owned;
+
+ /* session interface notes:
+ * the stub base itself implements i_sessionmessage and its associated
+ * i_session. the client or server implementor implements, or not, the
+ * objession interface methods, which are the callbacks to which the
+ * i_session calls will be forwarded, if present, to the implementation.
+ * when we construct a stub, we copy the implementor's objsession* to
+ * the stub's impl_callbacks member for convenience. so, the presence
+ * of objession callbacks indicates the desire to have exceptions, etc.
+ * forwarded to the implementation.
+ */
+} etch_stub;
+
+
+/*
+ * etchstub_runparams
+ * parameters to a stub helper thread.
+ */
+typedef struct etchstub_runparams
+{
+ unsigned int sig; /* signature */
+ etch_stub* stub; /* stub's this pointer */
+ opaque_stubhelper run; /* stub helper function */
+ void* server; /* i_xxx_server impl mask */
+ i_delivery_service* ds;
+ etch_who* who;
+ etch_message* msg;
+
+} etchstub_runparams;
+
+#define ETCH_STUBPARAMS_SIGNATURE 0xe1d2c3b4
+
+
+etch_stub* new_stub (void* thisx, unsigned char stubtype,
+ i_delivery_service*, etch_threadpool* qp, etch_threadpool* fp);
+
+void* new_clientstub_init (void* implobj, const int bytelen, etch_object_destructor userdtor,
+ i_delivery_service*, etch_threadpool* qp, etch_threadpool* fp, void*);
+
+void* new_serverstub_init (void* implobj, const int bytelen, etch_object_destructor userdtor,
+ i_delivery_service*, etch_threadpool* qp, etch_threadpool* fp, void*);
+
+void* new_stubimpl_init (void* implobj, const int objsize,
+ const unsigned char stubtype, etch_object_destructor userdtor,
+ i_delivery_service* ids, etch_threadpool* qp, etch_threadpool* fp,
+ void* params);
+
+int etchstub_validate_args (etch_stub*, i_delivery_service*, etch_message*,
+ void* iclient, default_value_factory**, void** vfimpl, void** clientimpl);
+
+int etchstub_send_reply (etch_stub*, i_delivery_service*,
+ etch_who*, etch_message*, etch_object*, boolean );
+
+int is_etch_stub(void*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_STUB_H */
diff --git a/binding-c/runtime/c/include/etch_svcobj_masks.h b/binding-c/runtime/c/include/etch_svcobj_masks.h
new file mode 100644
index 0000000..a4e10f3
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_svcobj_masks.h
@@ -0,0 +1,339 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_svcobj_masks.h
+ * masks over generated objects
+ */
+
+#ifndef ETCHSVCetch_objectS_H
+#define ETCHSVCetch_objectS_H
+
+#include "etch_transport.h"
+#include "etch_stub.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * i_xxx_server
+ * server base interface
+ * not instantiated, mask over a server base.
+ */
+typedef struct i_xxxx_server
+{
+ etch_object object;
+
+ etch_object* thisx; /* xxxx_server_impl* */
+ etch_object* ixxxx; /* service interface */
+ int session_id;
+ unsigned char is_service_interface_owned;
+ unsigned char unused[3];
+
+ /* - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - -
+ */
+ i_objsession* iobjsession;
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * methods and data specific to the service follow in the implementation
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+} i_xxxx_server;
+
+
+
+/**
+ * i_xxxx_client
+ * xxxx client base interface
+ * this object is not instantiated, it is merely a mask over any
+ * i_xxxx_client object, where xxxx is the etch service
+ */
+typedef struct i_xxxx_client
+{
+ etch_object object;
+
+ etch_object* thisx; /* xxxx_client_impl* */
+ etch_object* ixxxx; /* service interface */
+ int server_id;
+
+ /* - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - -
+ */
+ i_objsession* iobjsession;
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * methods and data specific to the service follow in the implementation
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+} i_xxxx_client;
+
+
+
+/**
+ * i_xxxx_either
+ * mask over xxxx client base or xxxx_server_base
+ */
+typedef struct i_xxxx_either
+{
+ etch_object object;
+
+ etch_object* thisx; /* xxxx_yyyy_impl* */
+ etch_object* ixxxx; /* service interface */
+ int owner_id;
+
+ /* - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - -
+ */
+ i_objsession* iobjsession;
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+} i_xxxx_either;
+
+
+/**
+ * xxxx_remote
+ * not instantiated, mask over any xxxx_remote object,
+ * where xxxx is the service name
+ */
+typedef struct xxxx_remote
+{
+ etch_object object;
+
+ etch_object* ixxxx; /* possibly owned */
+ i_delivery_service* dsvc; /* not owned */
+ etch_value_factory* vf; /* not owned */
+ unsigned char remote_type; /* client or server */
+ unsigned char is_service_interface_owned;
+ unsigned short unused; /* alignment */
+
+ etch_message* (*new_message) (void*, etch_type*);
+ int (*send) (void*, etch_message*);
+ void* (*sendex) (void*, etch_message*);
+ etch_delivsvc_begincall begin_call; /* i_mailbox** out */
+ etch_delvisvc_endcall end_call; /* etch_object** out */
+
+ int (*start_waitup) (void*, const int waitms);
+ int (*stop_waitdown) (void*, const int waitms);
+
+ /* - - - - - - - - - - - - -
+ * transport functionality
+ * - - - - - - - - - - - - -
+ */
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * methods and data specific to the service follow in the implementation
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+} xxxx_remote;
+
+typedef xxxx_remote etch_remote;
+
+
+
+/**
+ * xxxx_remote_client
+ * this object is not instantiated, it is merely a mask over any
+ * xxxx_remote_client object, where xxxx is the etch service
+ */
+typedef struct xxxx_remote_client
+{
+ etch_object object;
+
+ i_xxxx_client* xxxx_client_base; /* owned */
+ xxxx_remote* xxxx_remote_base; /* owned */
+ etch_server_factory* server_factory; /* owned */
+ default_value_factory* vf; /* owned by base */
+ int session_id;
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * methods and data specific to the service follow in the implementation
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+} xxxx_remote_client;
+
+
+
+/**
+ * xxxx_remote_server
+ */
+typedef struct xxxx_remote_server
+{
+ etch_object object;
+
+ i_xxxx_server* server_base; /* owned */
+ xxxx_remote* remote_base; /* owned */
+ etch_client_factory* client_factory; /* owned */
+ default_value_factory* vf; /* owned by base */
+ int server_id;
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * methods and data specific to the service follow in the implementation
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+} xxxx_remote_server;
+
+
+/**
+ * xxxx_remote_either
+ */
+typedef struct xxxx_remote_either
+{
+ etch_object object;
+
+ i_xxxx_either* either_base; /* owned */
+ xxxx_remote* remote_base; /* owned */
+ void* either_factory; /* owned */
+ default_value_factory* vf; /* owned by base */
+ int owner_id;
+
+} xxxx_remote_either;
+
+
+/**
+ * xxxx_either_stub
+ * mask over xxxx_client_stub or xxxx_server_stub
+ */
+typedef struct xxxx_either_stub
+{
+ etch_object object;
+
+ etch_stub* stub_base;
+ int owner_id; /* e.g. session_id */
+
+ int (*destroyex)(void*); /* user memory destructor */
+
+} xxxx_either_stub;
+
+/**
+ * xxxx_server_impl
+ * this object is not instantiated, it is merely a mask over any
+ * xxxx_server_impl object, where xxxx is the etch service
+ */
+typedef struct xxxx_server_impl
+{
+ etch_object object;
+ i_xxxx_server* server_base; /* owned */
+ etch_object* ixxxx; /* not owned */
+ xxxx_remote_client* client; /* not owned */
+
+ int (*destroyex) (void*); /* user memory destructor */
+
+ /* - - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - - -
+ */
+ i_objsession* iobjsession; /* owned by base */
+ /* note that iobjsession->thisx is set to this xxxx_server_impl* */
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * methods and data specific to the service follow in the implementation
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+} xxxx_server_impl;
+
+/**
+ * xxxx_client_impl
+ * this object is not instantiated, it is merely a mask over any
+ * xxxx_client_impl object, where xxxx is the etch service
+ */
+typedef struct xxxx_client_impl
+{
+ etch_object object;
+
+ i_xxxx_client* client_base; /* owned */
+ etch_object* ixxxx; /* not owned */
+ xxxx_remote_server* server; /* not owned */
+
+ int (*destroyex) (void*); /* user memory destructor */
+
+ /* - - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - - -
+ */
+ i_objsession* iobjsession; /* owned by base */
+ /* note that iobjsession->thisx is set to this perf_client_impl* */
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * methods and data specific to the service follow in the implementation
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+} xxxx_client_impl;
+
+
+/**
+ * xxxx_either_impl
+ * mask over any xxxx_client_impl or xxxx_server_impl
+ */
+typedef struct xxxx_either_impl
+{
+ etch_object object;
+
+ i_xxxx_either* either_base; /* owned */
+ etch_object* ixxxx; /* not owned */
+ xxxx_remote_either* either; /* not owned */
+
+ int (*destroyex) (void*); /* user memory destructor */
+
+ /* - - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - - -
+ */
+ i_objsession* iobjsession; /* owned by base */
+ /* note that iobjsession->thisx is set to this perf_client_impl* */
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+} xxxx_either_impl;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHSVCetch_objectS_H */
diff --git a/binding-c/runtime/c/include/etch_tagdata_inp.h b/binding-c/runtime/c/include/etch_tagdata_inp.h
new file mode 100644
index 0000000..945d7f0
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_tagdata_inp.h
@@ -0,0 +1,104 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_tagdata_inp.h
+ * tagged data input base class
+ */
+
+#ifndef ETCH_TAGDATA_INP_H
+#define ETCH_TAGDATA_INP_H
+
+#include "etch_msgutl.h"
+#include "etch_tagged_data.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct etch_message;
+struct etch_structvalue;
+struct etch_arrayvalue;
+struct etch_flexbuffer;
+
+/**
+ * tagged_data_input
+ * tagged data input implementation
+ */
+typedef struct tagged_data_input
+{
+ etch_object object;
+
+ etch_object* impl;
+
+} tagged_data_input;
+
+
+/**
+ * i_tagged_data_input virtual function signatures
+ */
+typedef struct etch_message* (*etchtdi_start_message)(tagged_data_input*);
+typedef struct etch_message* (*etchtdi_read_message) (tagged_data_input*, struct etch_flexbuffer*);
+typedef int (*etchtdi_end_message) (tagged_data_input*, struct etch_message*);
+
+typedef struct etch_structvalue*(*etchtdi_start_struct) (tagged_data_input*);
+typedef struct etch_structvalue*(*etchtdi_read_struct) (tagged_data_input*);
+typedef int (*etchtdi_read_struct_element) (tagged_data_input*, struct etch_struct_element*);
+typedef int (*etchtdi_end_struct) (tagged_data_input*, struct etch_structvalue*);
+
+typedef struct etch_arrayvalue* (*etchtdi_start_array)(tagged_data_input*);
+typedef struct etch_arrayvalue* (*etchtdi_read_array) (tagged_data_input*, struct etch_validator*);
+typedef int (*etchtdi_read_array_element) (tagged_data_input*, ETCH_ARRAY_ELEMENT**);
+typedef int (*etchtdi_end_array)(tagged_data_input*, struct etch_arrayvalue*);
+
+
+/**
+ * i_tagged_data_input
+ * virtual function table for tagged data input
+ */
+struct i_tagged_data_input
+{
+ etch_object object;
+
+ etchparentinfo* inherits_from;
+
+ etchtdi_start_message start_message;
+ etchtdi_read_message read_message;
+ etchtdi_end_message end_message;
+ etchtdi_start_struct start_struct;
+ etchtdi_read_struct read_struct;
+ etchtdi_end_struct end_struct;
+ etchtdi_start_array start_array;
+ etchtdi_read_array read_array;
+ etchtdi_end_array end_array;
+
+ tagdata_get_native_type_code get_native_type_code;
+ tagdata_get_custom_structtype get_custom_structtype;
+ tagdata_check_value check_value;
+};
+
+typedef struct i_tagged_data_input i_tagged_data_input;
+
+
+tagged_data_input* new_tagged_data_input();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_TAGDATA_INP_H */
diff --git a/binding-c/runtime/c/include/etch_tagdata_out.h b/binding-c/runtime/c/include/etch_tagdata_out.h
new file mode 100644
index 0000000..d0b8035
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_tagdata_out.h
@@ -0,0 +1,94 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_tagdata_out.h
+ * tagged data output
+ */
+
+#ifndef ETCH_TAGDATA_OUT_H
+#define ETCH_TAGDATA_OUT_H
+
+#include "etch_msgutl.h"
+#include "etch_tagged_data.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct etch_message;
+struct etch_structvalue;
+struct etch_arrayvalue;
+struct etch_flexbuffer;
+
+/**
+ * tagged_data_output
+ * tagged data output implementation
+ */
+typedef struct tagged_data_output
+{
+ etch_object object;
+
+ etch_object* impl;
+} tagged_data_output;
+
+/**
+ * i_tagged_data_output virtual function signatures
+ */
+typedef int (*etchtdo_start_message)(tagged_data_output*, struct etch_message*);
+typedef int (*etchtdo_write_message)(tagged_data_output*, struct etch_message*, struct etch_flexbuffer*);
+typedef int (*etchtdo_end_message) (tagged_data_output*, struct etch_message*);
+
+typedef int (*etchtdo_start_struct) (tagged_data_output*, struct etch_structvalue*);
+typedef int (*etchtdo_write_struct) (tagged_data_output*, struct etch_structvalue*);
+typedef int (*etchtdo_end_struct) (tagged_data_output*, struct etch_structvalue*);
+
+typedef int (*etchtdo_start_array) (tagged_data_output*, struct etch_arrayvalue*);
+typedef int (*etchtdo_write_array) (tagged_data_output*, struct etch_arrayvalue*, struct etch_validator*);
+typedef int (*etchtdo_end_array) (tagged_data_output*, struct etch_arrayvalue*);
+
+
+/**
+ * i_tagged_data_output
+ */
+struct i_tagged_data_output
+{
+ etch_object object;
+
+ etchparentinfo* inherits_from;
+
+ etchtdo_start_message start_message;
+ etchtdo_write_message write_message;
+ etchtdo_end_message end_message;
+ etchtdo_start_struct start_struct;
+ etchtdo_write_struct write_struct;
+ etchtdo_end_struct end_struct;
+ etchtdo_start_array start_array;
+ etchtdo_write_array write_array;
+ etchtdo_end_array end_array;
+};
+
+typedef struct i_tagged_data_output i_tagged_data_output;
+
+tagged_data_output* new_tagged_data_output();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_TAGDATA_OUT_H */
diff --git a/binding-c/runtime/c/include/etch_tagged_data.h b/binding-c/runtime/c/include/etch_tagged_data.h
new file mode 100644
index 0000000..66045b4
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_tagged_data.h
@@ -0,0 +1,201 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_tagdata.h
+ * tagged data
+ */
+
+#ifndef ETCH_TAGDATA_H
+#define ETCH_TAGDATA_H
+
+#include "etch_object.h"
+#include "etch_type.h"
+#include "etch_validator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct etch_arrayvalue;
+
+#define ETCH_BINTAGDATA_CURRENT_VERSION 3
+
+/**
+ * i_tagdata virtual function signatures
+ */
+typedef signed char (*tagdata_check_value) (etch_object* value);
+typedef signed char (*tagdata_get_native_type_code) (const unsigned short obj_type, const unsigned short class_id);
+typedef int (*tagdata_get_native_type)(const signed char, etch_array_id_params*);
+typedef etch_type* (*tagdata_get_custom_structtype) (etch_object* thisx, const unsigned short obj_type, const unsigned short class_id);
+
+/**
+ * tagged data static function signatures
+ */
+struct etch_arrayvalue* etchtagdata_to_arrayvalue(etch_object* thisx, struct etch_nativearray* value, tagdata_get_native_type_code get_native_type_code);
+
+int etchtagdata_adjust_small_value (const byte in, etch_object* value, signed char* out);
+int etchtagdata_validate_value(etch_object* valobj, struct etch_validator*, signed char* out);
+int etchtagdata_get_number(etch_object* valobj, const double fminval, const double fmaxval, int64* out);
+int etchtagdata_get_double_number(etch_object* valobj, const double fmin, const double fmax, double* outd);
+int etchtagdata_get_float_number(etch_object* valobj, const double fmin, const double fmax, float* outf);
+int etchtagdata_bool_value (etch_object* valobj, boolean* out);
+int etchtagdata_byte_value (etch_object* valobj, byte* out);
+int etchtagdata_int16_value (etch_object* valobj, short* out);
+int etchtagdata_int32_value (etch_object* valobj, int* out);
+int etchtagdata_int64_value (etch_object* valobj, int64* out);
+int etchtagdata_float_value (etch_object* valobj, float* out);
+int etchtagdata_double_value(etch_object* valobj, double* out);
+int etchtagdata_is_eod (etch_object*);
+int etchtagdata_is_null(etch_object*);
+etch_object* etchtagdata_new_eodmarker(const int is_static);
+etch_object* etchtagdata_new_nullobj (const int is_static);
+etch_string* etchtagdata_new_emptystring(const int is_static);
+signed char etchtagdata_check_value (etch_object*);
+signed char etchtagdata_check_byte (const signed char);
+signed char etchtagdata_check_short (const short);
+signed char etchtagdata_check_integer (const int);
+signed char etchtagdata_check_long (const int64);
+
+
+/**
+ * etch_typecode
+ * enumeration of values denoting type of an encoded value
+ * rage 1: -128 to -65 defined types
+ * range 2: -64 to 127 tiny integer values
+ */
+typedef enum etch_typecode
+{
+ /**
+ * denotes a null value.
+ */
+ ETCH_XTRNL_TYPECODE_NULL = -128,
+ /**
+ * denotes no value, which is different than null. for example, an array is a
+ * sequence of values (some of which may be null) terminated by a NONE.
+ */
+ ETCH_XTRNL_TYPECODE_NONE = -127,
+ ETCH_XTRNL_TYPECODE_ENDDATA = ETCH_XTRNL_TYPECODE_NONE,
+
+ /* - - - - - - - - - - - - - - - - - -
+ * scalars (also see tiny int below)
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+ /**
+ * denotes a false boolean value.
+ */
+ ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE = -126,
+ /**
+ * denotes a true boolean value.
+ */
+ ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE = -125,
+ ETCH_XTRNL_TYPECODE_BOOLEAN_CONTENT = ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE,
+
+ /**
+ * signed byte.
+ */
+ ETCH_XTRNL_TYPECODE_BYTE = -124,
+ /**
+ * two byte signed short, msb first
+ */
+ ETCH_XTRNL_TYPECODE_SHORT = -123,
+ /**
+ * four byte signed integer, msb first.
+ */
+ ETCH_XTRNL_TYPECODE_INT = -122,
+ /**
+ * eight byte byte signed long, msb first
+ */
+ ETCH_XTRNL_TYPECODE_LONG = -121,
+ /**
+ * four byte ieee floating format number
+ */
+ ETCH_XTRNL_TYPECODE_FLOAT = -120,
+ /**
+ * eight byte ieee floating format number.
+ */
+ ETCH_XTRNL_TYPECODE_DOUBLE = -119,
+
+ /* - - - - - - - - - - - - - - - - - -
+ * arrays
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+ /**
+ * an array of bytes.
+ */
+ ETCH_XTRNL_TYPECODE_BYTES = -117,
+ /**
+ * a sequence of values.
+ */
+ ETCH_XTRNL_TYPECODE_ARRAY = -111,
+
+ /* - - - - - - - - - - - - - - - - - -
+ * strings
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+ /**
+ * denotes an empty string.
+ */
+ ETCH_XTRNL_TYPECODE_EMPTY_STRING = -110,
+ /**
+ * utf-8 encoded string
+ */
+ ETCH_XTRNL_TYPECODE_STRING = -109,
+
+ /* - - - - - - - - - - - - - - - - - -
+ * structures
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+ /**
+ * a sequence of key / value pairs.
+ */
+ ETCH_XTRNL_TYPECODE_STRUCT = -108,
+
+ /**
+ * a custom value from a value factory (struct, exception, enum, extern).
+ * an associated value identifies the specific type. the wire format of
+ * STRUCT and CUSTOM are the same, i.e. can use CUSTOM in place of STRUCT
+ * and accept STRUCT as if CUSTOM.
+ */
+ ETCH_XTRNL_TYPECODE_CUSTOM = -107,
+ /**
+ * denotes that any value is ok (as long as we know how to serialize it).
+ * dynamic methods should be applied to determine type.
+ * this type never appears on the wire.
+ */
+ ETCH_XTRNL_TYPECODE_ANY = -106,
+
+ /* - - - - - - - - - - - - - - - - - -
+ * tiny integers (no value follows)
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+ ETCH_XTRNL_TYPECODE_MIN_TINY_INT = -64,
+ ETCH_XTRNL_TYPECODE_MAX_TINY_INT = 127,
+
+} etch_typecode;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_TAGDATA_H */
diff --git a/binding-c/runtime/c/include/etch_tcp_connection.h b/binding-c/runtime/c/include/etch_tcp_connection.h
new file mode 100644
index 0000000..5ad127d
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_tcp_connection.h
@@ -0,0 +1,119 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_connection.h
+ * connection client and server classes - tcp, udp
+ */
+
+#ifndef _ETCH_TCP_CONNECTION_H_
+#define _ETCH_TCP_CONNECTION_H_
+
+#include "apr_thread_proc.h"
+#include "etch_connection.h"
+#include "etch_transport_data.h"
+#include "etch_transportint.h"
+#include "etch_session_data.h"
+#include "etch_url.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct etch_tcp_client;
+
+/*
+ * tcp specific connection type
+ */
+typedef struct etch_tcp_connection
+{
+ etch_object object;
+
+ etch_connection cx;
+ struct etch_tcp_client* rcvlxr; /* owned */
+
+ i_transportdata* itd; /* owned */
+
+ i_sessiondata* session; /* not owned */
+
+ int linger;
+ int traffic_class;
+
+ unsigned char is_nodelay;
+ unsigned char is_keepalive;
+ unsigned char is_autoflush;
+ unsigned char is_session_owned;
+
+} etch_tcp_connection;
+
+
+/*
+ * udp connection
+ * placeholder - this will go in etch_udpconxn.h when we implement udp transport
+ */
+typedef struct etch_udp_connection
+{
+ etch_object object;
+
+ etch_connection cx;
+
+ apr_sockaddr_t *last_from;
+
+} etch_udp_connection;
+
+
+/**
+ * etch_tcp_client
+ * tcp client listener class
+ */
+typedef struct etch_tcp_client
+{
+ etch_object object;
+
+ etch_tcp_connection* cxlisten; /* not owned */
+ etch_thread* thread;
+ unsigned char is_started;
+
+} etch_tcp_client;
+
+
+extern unsigned connection_id_farm;
+
+etch_tcp_connection* new_tcp_connection(etch_url*, void* resources, etch_rawsocket*);
+int init_etch_tcpconx_interfaces (etch_tcp_connection*);
+int new_tcpsocket (apr_socket_t**, apr_pool_t*);
+int destroy_etch_tcp_connection(void* thisx);
+int etch_tcpconx_start(etch_tcp_connection*);
+int etch_tcpclient_start_listener (etch_tcp_connection*);
+int etch_tcpconx_open (etch_tcp_connection*, const int is_reconnect);
+int etch_tcpconx_close(etch_tcp_connection*, const int is_linger);
+int etch_tcpconx_wait_until(etch_tcp_connection*, const int64 waitval, const int timeoutms);
+int etch_tcpconx_on_event(void*, const int e, int p1, void* p2);
+int etch_tcpclient_send(etch_tcp_connection *conx, unsigned char* buf, const size_t totallen, int* rc);
+int etch_tcpclient_sendex (etch_tcp_connection *conx, unsigned char* buf, const size_t totallen, const int timeout_ms, int* rc);
+int etch_tcpclient_receive (etch_tcp_connection*, unsigned char*, const size_t, int*);
+int etch_tcpclient_receivex(etch_tcp_connection*, unsigned char*, const size_t, const int, int*);
+int etch_tcpclient_stop(etch_tcp_connection*);
+int etch_tcpclient_stop_listener (etch_tcp_connection*);
+int is_good_tcp_params(etch_url*, void* resources, etch_rawsocket*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHTCPCONXN_H */
diff --git a/binding-c/runtime/c/include/etch_tcp_server.h b/binding-c/runtime/c/include/etch_tcp_server.h
new file mode 100644
index 0000000..15dd2d0
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_tcp_server.h
@@ -0,0 +1,120 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_connection.h
+ * connection client and server classes - tcp, udp
+ */
+
+#ifndef ETCHTCPSERVER_H
+#define ETCHTCPSERVER_H
+
+#include "apr_thread_proc.h"
+#include "etch_tcp_connection.h"
+#include "etch_resources.h"
+#include "etch_session_listener.h"
+#include "etch_mutex.h"
+#include "etch_linked_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct etch_tcp_server;
+typedef int (*etch_listener_event_handler) (struct etch_tcp_server*, struct etch_tcp_connection*, const int, int, void*);
+
+#define ETCH_TCPSERVER_STATE_CLOSED 0
+#define ETCH_TCPSERVER_STATE_CLOSING 1
+#define ETCH_TCPSERVER_STATE_STOPPED 2
+#define ETCH_TCPSERVER_STATE_STOPPING 3
+#define ETCH_TCPSERVER_STATE_STARTING 4
+#define ETCH_TCPSERVER_STATE_STARTED 5
+
+
+/**
+ * etch_tcp_server
+ * tcp listener class
+ */
+typedef struct etch_tcp_server
+{
+ etch_object object;
+
+ etch_tcp_connection* cxlisten; /* owned */
+ etch_threadpool* threadpool; /* not owned */
+ etch_threadpool* subpool; /* not owned */
+ etch_listener_event_handler on_event;
+ etch_connection_event_handler on_data;
+
+ /* - - - - - - - - - - - - - - - - - -
+ * i_sessionlistener - set externally
+ * - - - - - - - - - - - - - - - - - -
+ */
+ i_session* isession; /* not owned */
+ i_sessionlistener* session; /* owned or not */
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+ etch_session_accepted on_session_accepted;
+
+ /* - - - - - - - - - - - - - - - - - -
+ * i_transport
+ * - - - - - - - - - - - - - - - - - -
+ */
+ i_transport* itransport; /* owned */
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ /* - - - - - - - - - - - - - - - - -
+ * instance data
+ * - - - - - - - - - - - - - - - - -
+ */
+ unsigned listener_id;
+ int backlog;
+ int connections;
+ etch_url* url;
+ etch_wait_t* waiter; /* not owned */
+ etch_resources* resxmap; /* not owned */
+
+ unsigned char state;
+ unsigned char is_started;
+ unsigned char is_session_owned;
+ unsigned char is_threadpool_owned;
+
+ etch_linked_list_t* client_connections;
+ etch_mutex* client_connections_mutex;
+ etch_thread* thread;
+
+} etch_tcp_server;
+
+unsigned tcpserver_id_farm;
+
+etch_tcp_server* new_tcp_server(etch_url*, etch_threadpool*, etch_threadpool*, etch_resources*, i_sessionlistener*);
+int etch_tcpsvr_open (etch_tcp_server*, const int is_reconnect);
+int etch_tcpsvr_start(etch_tcp_server*);
+int etch_tcpsvr_stop (etch_tcp_server*);
+int etch_tcpsvr_close(etch_tcp_server*);
+int etch_deftcplistener_on_event(etch_tcp_server*, etch_tcp_connection*, const int e, int p1, void* p2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHTCPSERVER_H */
diff --git a/binding-c/runtime/c/include/etch_tdformat.h b/binding-c/runtime/c/include/etch_tdformat.h
new file mode 100644
index 0000000..c2f70bf
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_tdformat.h
@@ -0,0 +1,69 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_tdformat.h
+ * a factory used to construct tagged data processors for messagizer
+ */
+
+#ifndef ETCHTDFORMAT_H
+#define ETCHTDFORMAT_H
+
+#include "etch_tagdata_inp.h"
+#include "etch_tagdata_out.h"
+#include "etch_binary_tdi.h"
+#include "etch_binary_tdo.h"
+#include "etch_value_factory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * tagdata_format_factory
+ */
+typedef struct tagdata_format_factory
+{
+ etch_object object;
+
+ tagged_data_input* (*new_tagdata_input) (etch_value_factory*);
+ tagged_data_output* (*new_tagdata_output) (etch_value_factory*);
+
+} tagdata_format_factory;
+
+typedef enum etchtdf /* tagged data format IDs */
+{ ETCH_TAGDATA_FORMAT_BINARY,
+ ETCH_TAGDATA_FORMAT_XML,
+ ETCH_TAGDATA_FORMAT_COUNT /* must be last in list */
+} etchtdf;
+
+const wchar_t* tdformat_names[ETCH_TAGDATA_FORMAT_COUNT];
+
+const wchar_t* tdfname_binary;
+const wchar_t* tdfname_xml;
+
+int is_tdf_initialized;
+
+tagdata_format_factory* get_format_factory(wchar_t* format_name);
+int etchtdf_name_to_id(wchar_t* format_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHTDFORMAT_H */
diff --git a/binding-c/runtime/c/include/etch_thread.h b/binding-c/runtime/c/include/etch_thread.h
new file mode 100644
index 0000000..46ab71b
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_thread.h
@@ -0,0 +1,206 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etchpool.h -- threads and thread pool
+ */
+
+#ifndef ETCHPOOL_H
+#define ETCHPOOL_H
+
+#include "apr_thread_proc.h"
+#include "apr_atomic.h"
+
+#include "etch_object.h"
+#include "etch_mutex.h"
+#include "etch_wait.h"
+#include "etch_arraylist.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_THREADSTATE_INITIAL 0
+#define ETCH_THREADSTATE_STARTED 4
+#define ETCH_THREADSTATE_STOPPING 7
+#define ETCH_THREADSTATE_STOPPED 8
+#define ETCH_THREADSTATE_DEFUNCT 9
+
+#define ETCH_DEFAULT_POOLTHREADS 4
+#define ETCH_THREAD_PARAMS_SIGNATURE 0xcafef00d
+
+//TODO: check global + do doc
+// move to c file
+// synchronize thread_id_farm var
+extern unsigned thread_id_farm;
+
+
+extern etch_mutex* loglock;
+
+
+unsigned next_etch_threadid();
+int etch_apr_init();
+struct etch_threadpool;
+struct etch_thread;
+int etch_init_static_mutex(etch_mutex*, const int is_nestable);
+
+struct etch_threadpool;
+
+/** signature of a thread procedure */
+typedef void (*etch_threadproc) (void*);
+
+/** signature of a thread procedure callback */
+typedef int (*etch_thread_callback) (void*);
+
+/** interface to run a pool thread */
+typedef struct etch_thread* (*etch_run) (struct etch_threadpool*, etch_threadproc, void*);
+
+/**
+ * etch_apr_threaddata
+ * thread parameters specific to the apache portable runtime.
+ * this object memory is always managed by the caller, regardless of
+ * thread params is_xxxx configuration.
+ */
+typedef struct etch_apr_threaddata
+{
+ apr_pool_t* mempool;
+ apr_thread_t* thread;
+ apr_threadattr_t* threadattr;
+ unsigned char threadstate;
+ unsigned char is_dedicated_mempool;
+
+} etch_apr_threaddata;
+
+
+/**
+ * etch_thread_params
+ * parameters to any thread start.
+ * an internal threadproc invokes the user threadproc,
+ * so we supply this object to the internal threadproc as data.
+ */
+typedef struct etch_thread_params
+{
+ unsigned int signature; /* thread params sig */
+ int etch_thread_id; /* our sequential ID */
+ etch_threadproc threadproc; /* user thread proc */
+ etch_thread_callback on_start; /* thread start hook */
+ etch_thread_callback on_exit; /* thread exit hook */
+ etch_apr_threaddata* libdata; /* thread library data*/
+ struct etch_threadpool* etchpool; /* thread pool if any */
+ struct etch_thread* etchobj; /* etch_thread object */
+ etch_wait_t* waitobj; /* post-start waiter */
+ void* data; /* actual thread data */
+ unsigned char is_own_data; /* does thread own data */
+ unsigned char is_data_etchobject; /* is destroy() in play */
+ unsigned char threadstate; /* current start state */
+ unsigned char unused;
+ etch_mutex_hookproc synchook;
+ etch_mutex_t* synclock;
+
+} etch_thread_params;
+
+
+/**
+ * etch_thread: thread object
+ */
+typedef struct etch_thread
+{
+ etch_object object;
+
+ unsigned char is_joined;
+
+ int (*start) (void*);
+ int (*stop) (void*);
+
+ etch_thread_params params;
+ etch_apr_threaddata aprdata;
+ etch_wait_t* waiter;
+ int64 startCond;
+ etch_mutex_t* objlock;
+
+} etch_thread;
+
+
+/**
+ * etch_threadpool: thread pool object
+ */
+typedef struct etch_threadpool
+{
+ etch_object object;
+
+ etch_run run;
+
+ etch_mutex* pool_lock;
+
+ /* to do: embed underlying queued threadpool here, such as APR pool */
+
+ etch_arraylist* threadlist;
+ etch_mutex* threadlist_lock;
+ unsigned int threadpool_id;
+ int (*count) (); /* implementation should return threadlist.count */
+
+ unsigned char pooltype; /* queued or free */
+ unsigned char is_defunct; /* is pool forcibly destroying threads */
+ unsigned char is_iterating; /* is pool currently being iterated */
+ unsigned char is_free_data; /* will thread free its data memory */
+ unsigned char is_free_threads; /* is poolthread memory freed */
+ unsigned char is_data_etchobject; /* can thread call destroy() on data */
+ unsigned char is_manual_start; /* is explicit thread.start() required */
+ unsigned char unused;
+
+} etch_threadpool;
+
+
+/*
+ * global free thread pool
+ */
+extern etch_threadpool* global_free_threadpool;
+etch_threadpool* etch_glopool();
+int etch_glopool_exit();
+
+extern unsigned short etch_threadpool_id_farm;
+
+#define ETCH_HAS_QUEUED_THREADPOOL (0)
+
+
+etch_threadpool* new_threadpool(const unsigned pooltype, const int initsize);
+etch_thread* new_thread (etch_threadproc, void*);
+etch_thread* threadpool_remove_entry (etch_threadpool*, const int thread_id);
+etch_thread* threadpool_thread (etch_threadpool*, const int i);
+int threadpool_waitfor_all(etch_threadpool*, const int is_cancel);
+int threadpool_remove (etch_threadpool*, const int id);
+int destroy_threadpool(void*);
+
+void etch_init_threadparams (etch_thread_params*);
+int etch_createthread (etch_thread_params*);
+int etch_thread_join (etch_thread_params*);
+int etch_thread_cancel(etch_thread*);
+int etch_thread_current_id();
+int etch_join(etch_thread*);
+void etch_thread_yield ();
+int etch_on_threadstart(void*);
+int etch_on_threadexit (void*);
+size_t etch_get_threadid (void* threadstruct);
+void etch_sleep(const int ms);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHPOOL_H */
diff --git a/binding-c/runtime/c/include/etch_thread2.h b/binding-c/runtime/c/include/etch_thread2.h
new file mode 100644
index 0000000..50f23df
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_thread2.h
@@ -0,0 +1,108 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_thread.h -- thread implementation
+ */
+
+#ifndef _ETCH_THREAD_H_
+#define _ETCH_THREAD_H
+
+#include "etch_errno.h"
+#include "etch_mem.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_THREAD_CREATE_JOINABLE 0
+#define ETCH_THREAD_CREATE_DETACHED 1
+
+typedef struct etch_thread_attr_t etch_thread_attr_t;
+typedef struct etch_thread_t etch_thread_t;
+
+typedef void (*etch_thread_func)(etch_thread_t* thread, void* data);
+
+/**
+ * create a new thread attr instance.
+ * @param attr
+ * @return status
+ */
+etch_status_t etch_thread_attr_create(etch_thread_attr_t** attr);
+
+/**
+ * set thread detach state.
+ * @param attr
+ * @param state ETCH_THREAD_CREATE_JOINABLE or ETCH_THREAD_CREATE_DETACHED
+ * @return status
+ */
+etch_status_t etch_thread_attr_set_detachstate(etch_thread_attr_t* attr, int state);
+
+/**
+ * get thread detach state.
+ * @param attr
+ * @param state ETCH_THREAD_CREATE_JOINABLE or ETCH_THREAD_CREATE_DETACHED
+ * @return status
+ */
+etch_status_t etch_thread_attr_get_detachstate(etch_thread_attr_t* attr, int* state);
+
+/**
+ * destroy thread attr instance.
+ * @param attr
+ * @return status
+ */
+etch_status_t etch_thread_attr_destroy(etch_thread_attr_t* attr);
+
+
+/**
+ * create a new thread.
+ * @param thread
+ * @param attr
+ * @param thread_proc
+ * @param data
+ * @return status
+ */
+etch_status_t etch_thread_create(etch_thread_t** thread, const etch_thread_attr_t* attr, etch_thread_func thread_proc, void* data);
+
+/**
+ * wait thread proc has finished.
+ * @param thread
+ * @param status return code of thread exit
+ * @return status
+ */
+etch_status_t etch_thread_join(etch_thread_t* thread, etch_status_t* status);
+
+/**
+ * yield current thread.
+ * @param status
+ */
+etch_status_t etch_thread_yield();
+
+/**
+ * thread exit, return status
+ * @param thread
+ * @param status thread exit status
+ * @return status
+ */
+etch_status_t etch_thread_exit(etch_thread_t* thread, etch_status_t status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef _ETCH_THREAD_H */
diff --git a/binding-c/runtime/c/include/etch_threadpool.h b/binding-c/runtime/c/include/etch_threadpool.h
new file mode 100644
index 0000000..2328159
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_threadpool.h
@@ -0,0 +1,46 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_threadpool.h -- etch thread pool
+ */
+
+#ifndef _ETCH_THREAD_POOL_H_
+#define _ETCH_THREAD_POOL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "etch_errno.h"
+
+#define ETCH_THREADPOOL_TYPE_FREE 0x01
+#define ETCH_THREADPOOL_TYPE_QUEUED 0x02
+
+typedef struct etch_threadpool_t etch_threadpool_t;
+
+etch_status_t etch_threadpool_create(etch_threadpool_t** threadpool, uint8 type, const uint16 init_size);
+etch_status_t etch_threadpool_remove(etch_threadpool_t* threadpool, const uint16 thread_id);
+etch_status_t etch_threadpool_join(etch_threadpool_t* threadpool);
+etch_status_t etch_threadpool_destroy(etch_threadpool_t* threadpool);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef _ETCH_THREAD_POOL_H_ */
\ No newline at end of file
diff --git a/binding-c/runtime/c/include/etch_threadpool_apr.h b/binding-c/runtime/c/include/etch_threadpool_apr.h
new file mode 100644
index 0000000..f698091
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_threadpool_apr.h
@@ -0,0 +1,298 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+#ifndef APU_THREAD_POOL_H
+#define APU_THREAD_POOL_H
+
+#include "apr_thread_proc.h"
+
+/**
+ * @file apr_thread_pool.h
+ * @brief APR Thread Pool Library
+
+ * @remarks This library implements a thread pool using apr_thread_t. A thread
+ * pool is a set of threads that can be created in advance or on demand until a
+ * maximum number. When a task is scheduled, the thread pool will find an idle
+ * thread to handle the task. In case all existing threads are busy and the
+ * number of tasks in the queue is higher than the adjustable threshold, the
+ * pool will try to create a new thread to serve the task if the maximum number
+ * has not been reached. Otherwise, the task will be put into a queue based on
+ * priority, which can be valued from 0 to 255, with higher values being served
+ * first. If there are tasks with the same priority, the new task might be put at
+ * the top or at the bottom - it depends on which function is used to put the task.
+ *
+ * @remarks There may be the case where the thread pool can use up to the maximum
+ * number of threads at peak load, but having those threads idle afterwards. A
+ * maximum number of idle threads can be set so that the extra idling threads will
+ * be terminated to save system resources.
+ */
+#if APR_HAS_THREADS
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @defgroup APR_Util_TP Thread Pool routines
+ * @ingroup APR_Util
+ * @{
+ */
+
+/** Opaque Thread Pool structure. */
+typedef struct apr_thread_pool apr_thread_pool_t;
+
+#define APR_THREAD_TASK_PRIORITY_LOWEST 0
+#define APR_THREAD_TASK_PRIORITY_LOW 63
+#define APR_THREAD_TASK_PRIORITY_NORMAL 127
+#define APR_THREAD_TASK_PRIORITY_HIGH 191
+#define APR_THREAD_TASK_PRIORITY_HIGHEST 255
+
+/**
+ * Create a thread pool
+ * @param me The pointer in which to return the newly created apr_thread_pool
+ * object, or NULL if thread pool creation fails.
+ * @param init_threads The number of threads to be created initially, this number
+ * will also be used as the initial value for the maximum number of idle threads.
+ * @param max_threads The maximum number of threads that can be created
+ * @param pool The pool to use
+ * @return APR_SUCCESS if the thread pool was created successfully. Otherwise,
+ * the error code.
+ */
+apr_status_t etch_apr_thread_pool_create(apr_thread_pool_t **me,
+ apr_size_t init_threads,
+ apr_size_t max_threads,
+ apr_pool_t *pool);
+
+/**
+ * Destroy the thread pool and stop all the threads
+ * @return APR_SUCCESS if all threads are stopped.
+ */
+apr_status_t etch_apr_thread_pool_destroy(apr_thread_pool_t *me);
+
+/**
+ * Schedule a task to the bottom of the tasks of same priority.
+ * @param me The thread pool
+ * @param func The task function
+ * @param param The parameter for the task function
+ * @param priority The priority of the task.
+ * @param owner Owner of this task.
+ * @return APR_SUCCESS if the task had been scheduled successfully
+ */
+apr_status_t etch_apr_thread_pool_push(apr_thread_pool_t *me,
+ apr_thread_start_t func,
+ void *param,
+ apr_byte_t priority,
+ void *owner);
+/**
+ * Schedule a task to be run after a delay
+ * @param me The thread pool
+ * @param func The task function
+ * @param param The parameter for the task function
+ * @param time Time in microseconds
+ * @param owner Owner of this task.
+ * @return APR_SUCCESS if the task had been scheduled successfully
+ */
+apr_status_t etch_apr_thread_pool_schedule(apr_thread_pool_t *me,
+ apr_thread_start_t func,
+ void *param,
+ apr_interval_time_t time,
+ void *owner);
+
+/**
+ * Schedule a task to the top of the tasks of same priority.
+ * @param me The thread pool
+ * @param func The task function
+ * @param param The parameter for the task function
+ * @param priority The priority of the task.
+ * @param owner Owner of this task.
+ * @return APR_SUCCESS if the task had been scheduled successfully
+ */
+apr_status_t etch_apr_thread_pool_top(apr_thread_pool_t *me,
+ apr_thread_start_t func,
+ void *param,
+ apr_byte_t priority,
+ void *owner);
+
+/**
+ * Cancel tasks submitted by the owner. If there is any task from the owner that
+ * is currently running, the function will spin until the task finished.
+ * @param me The thread pool
+ * @param owner Owner of the task
+ * @return APR_SUCCESS if the task has been cancelled successfully
+ * @note The task function should not be calling cancel, otherwise the function
+ * may get stuck forever. The function assert if it detect such a case.
+ */
+apr_status_t etch_apr_thread_pool_tasks_cancel(apr_thread_pool_t *me,
+ void *owner);
+
+/**
+ * Get the current number of tasks waiting in the queue
+ * @param me The thread pool
+ * @return Number of tasks in the queue
+ */
+apr_size_t etch_apr_thread_pool_tasks_count(apr_thread_pool_t *me);
+
+/**
+ * Get the current number of scheduled tasks waiting in the queue
+ * @param me The thread pool
+ * @return Number of scheduled tasks in the queue
+ */
+apr_size_t etch_apr_thread_pool_scheduled_tasks_count(apr_thread_pool_t *me);
+
+/**
+ * Get the current number of threads
+ * @param me The thread pool
+ * @return Total number of threads
+ */
+apr_size_t etch_apr_thread_pool_threads_count(apr_thread_pool_t *me);
+
+/**
+ * Get the current number of busy threads
+ * @param me The thread pool
+ * @return Number of busy threads
+ */
+apr_size_t etch_apr_thread_pool_busy_count(apr_thread_pool_t *me);
+
+/**
+ * Get the current number of idle threads
+ * @param me The thread pool
+ * @return Number of idle threads
+ */
+apr_size_t etch_apr_thread_pool_idle_count(apr_thread_pool_t *me);
+
+/**
+ * Access function for the maximum number of idle threads. Number of current
+ * idle threads will be reduced to the new limit.
+ * @param me The thread pool
+ * @param cnt The number
+ * @return The number of threads that were stopped.
+ */
+apr_size_t etch_apr_thread_pool_idle_max_set(apr_thread_pool_t *me,
+ apr_size_t cnt);
+
+/**
+ * Get number of tasks that have run
+ * @param me The thread pool
+ * @return Number of tasks that have run
+ */
+apr_size_t
+ etch_apr_thread_pool_tasks_run_count(apr_thread_pool_t * me);
+
+/**
+ * Get high water mark of the number of tasks waiting to run
+ * @param me The thread pool
+ * @return High water mark of tasks waiting to run
+ */
+apr_size_t
+ etch_apr_thread_pool_tasks_high_count(apr_thread_pool_t * me);
+
+/**
+ * Get high water mark of the number of threads
+ * @param me The thread pool
+ * @return High water mark of threads in thread pool
+ */
+apr_size_t
+ etch_apr_thread_pool_threads_high_count(apr_thread_pool_t * me);
+
+/**
+ * Get the number of idle threads that were destroyed after timing out
+ * @param me The thread pool
+ * @return Number of idle threads that timed out
+ */
+apr_size_t
+ etch_apr_thread_pool_threads_idle_timeout_count(apr_thread_pool_t * me);
+
+/**
+ * Access function for the maximum number of idle threads
+ * @param me The thread pool
+ * @return The current maximum number
+ */
+apr_size_t etch_apr_thread_pool_idle_max_get(apr_thread_pool_t *me);
+
+/**
+ * Access function for the maximum number of threads.
+ * @param me The thread pool
+ * @param cnt Number of threads
+ * @return The original maximum number of threads
+ */
+apr_size_t etch_apr_thread_pool_thread_max_set(apr_thread_pool_t *me,
+ apr_size_t cnt);
+
+/**
+ * Access function for the maximum wait time (in microseconds) of an
+ * idling thread that exceeds the maximum number of idling threads.
+ * A non-zero value allows for the reaping of idling threads to shrink
+ * over time. Which helps reduce thrashing.
+ * @param me The thread pool
+ * @param timeout The number of microseconds an idle thread should wait
+ * till it reaps itself
+ * @return The original maximum wait time
+ */
+apr_interval_time_t
+ etch_apr_thread_pool_idle_wait_set(apr_thread_pool_t * me,
+ apr_interval_time_t timeout);
+
+/**
+ * Access function for the maximum wait time (in microseconds) of an
+ * idling thread that exceeds the maximum number of idling threads
+ * @param me The thread pool
+ * @return The current maximum wait time
+ */
+apr_interval_time_t
+ etch_apr_thread_pool_idle_wait_get(apr_thread_pool_t * me);
+
+/**
+ * Access function for the maximum number of threads
+ * @param me The thread pool
+ * @return The current maximum number
+ */
+apr_size_t etch_apr_thread_pool_thread_max_get(apr_thread_pool_t *me);
+
+/**
+ * Access function for the threshold of tasks in queue to trigger a new thread.
+ * @param me The thread pool
+ * @param cnt The new threshold
+ * @return The original threshold
+ */
+apr_size_t etch_apr_thread_pool_threshold_set(apr_thread_pool_t *me,
+ apr_size_t val);
+
+/**
+ * Access function for the threshold of tasks in queue to trigger a new thread.
+ * @param me The thread pool
+ * @return The current threshold
+ */
+apr_size_t etch_apr_thread_pool_threshold_get(apr_thread_pool_t * me);
+
+/**
+ * Get owner of the task currently been executed by the thread.
+ * @param thd The thread is executing a task
+ * @param owner Pointer to receive owner of the task.
+ * @return APR_SUCCESS if the owner is retrieved successfully
+ */
+apr_status_t etch_apr_thread_pool_task_owner_get(apr_thread_t *thd,
+ void **owner);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APR_HAS_THREADS */
+#endif /* !APR_THREAD_POOL_H */
diff --git a/binding-c/runtime/c/include/etch_transport.h b/binding-c/runtime/c/include/etch_transport.h
new file mode 100644
index 0000000..b5d98e7
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_transport.h
@@ -0,0 +1,282 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_transport.h -- common transport code
+ */
+
+#ifndef ETCHTRANSPORT_H
+#define ETCHTRANSPORT_H
+
+#include "etch_message.h"
+#include "etch_tcp_connection.h"
+#include "etch_resources.h"
+#include "etch_arraylist.h"
+#include "etch_mailbox_manager.h"
+#include "etch_session_listener.h"
+#include "etch_wait.h"
+#include "etch_url.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*etch_delivsvc_begincall)(void* thisx, etch_message*, void** out); /* i_mailbox** out */
+typedef int (*etch_delvisvc_endcall) (void* thisx, i_mailbox*, etch_type* response_type, void** out); /* etch_object** out */
+
+typedef void* (*new_client_impl_funcptr)(void* argstruct); /* not used */
+typedef void* (*main_client_create_func)(void* factory_thisx, void* server);
+
+typedef void* (*helper_listener_create_func)(void* serverargs, void* sessionargs);
+typedef int (*helper_resources_init_func)(void* argstruct);
+typedef void* (*main_server_create_func)(void* factory_thisx, void* client);
+
+/*
+ * i_delivery_service
+ * the implementor of delivery service will implement i_sessionmessage and
+ * i_session, and copy pointers to the implementations to this interface.
+ */
+typedef struct i_delivery_service
+{
+ etch_object object;
+
+ etch_delivsvc_begincall begin_call;
+ etch_delvisvc_endcall end_call;
+
+ i_sessionmessage* session; /* not owned */
+ i_transportmessage* transport; /* not owned */
+ i_sessionmessage* ism; /* not owned */
+ i_transportmessage* itm; /* not owned */
+
+ void* thisx;
+ etch_mutex* rwlock; /* not owned */
+ unsigned char is_session_owned;
+ unsigned char is_transport_owned;
+
+} i_delivery_service;
+
+
+/*
+ * etch_tcp_delivery_service
+ * java binding does not have one of these, it constructs and returns a
+ * DeliveryService in TcpTransportFactory.newTransport(). we will instead
+ * invoke new_etch_transport(uri, resources), returning an i_delivery_service.
+ */
+typedef struct etch_tcp_delivery_service
+{
+ etch_object object;
+
+ // ***** TODO lose i_deliveryservice instantiation and instead mask
+ // ***** all delivery service with i_deliveryservice. *************
+ i_delivery_service* ids;
+ etch_delivsvc_begincall begin_call;
+ etch_delvisvc_endcall end_call;
+
+ /* i_transportmessage - owns the tcp delivery service */
+ i_transportmessage* transport; /* owned by transportx */
+ i_mailbox_manager* transportx; /* owned */
+
+ /* external implementation of i_session_message */
+ i_sessionmessage* session; /* not owned */
+
+ /* delivery service implementation of i_session_message */
+ i_sessionmessage* sessionmsg; /* owned */
+ etch_session_message session_message;
+ etch_session_control session_control;
+ etch_session_notify session_notify;
+ etch_session_query session_query;
+
+ /* delivery service implementation of i_transport_message */
+ /* owns the tcp delivery service */
+ i_transportmessage* transportmsg; /* owned */
+ etch_transport_message transport_message;
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ /* private component implementations */
+ etch_resources* resources; /* not owned */
+ etch_tcp_connection* tcpconx; /* not owned on server - destroyed on listener exit */
+ etch_mutex* rwlock; /* not owned */
+ etch_wait_t* wait_up; /* owned by tcpconx */
+ etch_wait_t* wait_down; /* owned by tcpconx */
+ void* packetizer; /* owned */
+ void* messagizer; /* owned */
+ void* mailboxmgr; /* owned */
+
+ unsigned char is_tcpconx_owned;
+ unsigned char unused[3];
+
+} etch_tcp_delivery_service;
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * factory parameter bundle
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etch_session
+ * server's per-client parameters.
+ * this object wraps all of a server's per-session instance data.
+ */
+typedef struct etch_session
+{
+ etch_object object;
+
+ void* thisx; /* host etch_factory_params* */
+
+ unsigned session_id; /* session key: same as cx.conxid */
+ /* fyi a session's receive thread is available at cx.thread */
+ etch_connection* cx; /* session client accepted connection */
+ i_delivery_service* ds; /* session delivery service */
+ void* client; /* xxxx_remote_client* */
+ void* server; /* i_xxxx_server */
+ void* server_stub; /* xxxx_server_stub* */
+ etch_object* conximpl; /* accepted conx impl obj e.g. tcp_connection* */
+
+ /* note that mainlistener.thisx is the server object e.g. etch_tcp_server
+ * and that serverobject.session is this i_sessionlistener */
+ i_sessionlistener* mainlistener; /* accept listener */
+
+} etch_session;
+
+
+/**
+ * etch_factory_params
+ * mask over server or client factory parameter bundle
+ */
+typedef struct etch_factory_params
+{
+ etch_object object;
+
+ void* thisx; /* params owner TODO populate this */
+
+ wchar_t* in_uri;
+ etch_resources* in_resx;
+ etch_value_factory* in_valufact;
+ etch_threadpool* mainpool;
+ etch_threadpool* subpool;
+ etch_threadpool* qpool;
+ etch_threadpool* fpool;
+ etch_mutex* mblock;
+ etch_object* session;
+ i_session* isession;
+ etch_arraylist* per_connection_params;
+
+ /* factory constructors */
+ void* helper_new_xxxx;
+ void* main_new_xxxx;
+
+} etch_factory_params;
+
+
+/**
+ * etch_server_factory
+ * virtual server constructors and associated parameters.
+ * masked by etch_factory_params
+ */
+typedef struct etch_server_factory
+{
+ etch_object object;
+
+ void* thisx; /* params owner TODO populate this */
+
+ wchar_t* in_uri;
+ etch_resources* in_resx;
+ etch_value_factory* in_valufact;
+ etch_threadpool* mainpool; /* main listener thread manager*/
+ etch_threadpool* subpool;
+ etch_threadpool* qpool; /* session thread manager */
+ etch_threadpool* fpool; /* session thread manager */
+ etch_mutex* mblock; /* utility lock */
+ etch_object* session; /* i_session_listener* */
+ i_session* isession; /* listener's i_session */
+ etch_arraylist* clientlist; /* create session in new_helper_accepted_server() */
+
+ /* pointer to xxxx_helper_listener_create() in xxxx_helper */
+ helper_listener_create_func helper_new_listener;
+
+ /* pointer to new_xxxx_server() in [main] */
+ main_server_create_func main_new_server;
+
+} etch_server_factory;
+
+
+/**
+ * etch_client_factory
+ * masked by etch_factory_params
+ */
+typedef struct etch_client_factory
+{
+ etch_object object;
+
+ void* thisx; /* params owner todo populate this */
+
+ wchar_t* in_uri;
+ etch_resources* in_resx;
+ etch_value_factory* in_valufact;
+ etch_threadpool* mainpool;
+ etch_threadpool* subpool;
+ etch_threadpool* qpool;
+ etch_threadpool* fpool;
+ etch_mutex* rwlock; /* mailbox read/write lock */
+ etch_object* session; /* unused? */
+ i_session* isession;/* unused? */
+ etch_arraylist* per_server; /* unused? */
+
+ main_client_create_func new_client;
+ new_client_impl_funcptr new_client_impl; /* unused */
+
+ int server_id;
+ void* server; /* xxxx_remote_server* use: is_etch_remote_server(x) */
+ void* iclient; /* i_xxxx_client use: is_etch_client_base(x) */
+ void* stub; /* xxxx_client_stub* */
+ i_delivery_service* dsvc; /* use: is_etch_ideliverysvc(x) */
+
+} etch_client_factory;
+
+
+etch_tcp_delivery_service* new_tcp_delivery_service (etch_url*, etch_factory_params*, etch_tcp_connection*);
+etch_tcp_delivery_service* get_etch_ds_impl (i_delivery_service*);
+
+etch_resources* etch_transport_resources_init(etch_resources*);
+
+etch_resources* get_etch_transport_resources(etch_resources*);
+
+etch_client_factory* new_client_factory (etch_object* session, i_session* isession, main_client_create_func client_create_func);
+etch_server_factory* new_server_factory (etch_object* session, i_session* isession, helper_listener_create_func helper_listener_create, main_server_create_func main_server_create);
+
+i_delivery_service* new_delivery_service_interface(i_sessionmessage*, i_transportmessage*);
+i_delivery_service* new_etch_transport (wchar_t* uri, etch_factory_params*, void* conximpl);
+i_delivery_service* new_etch_transport_a(etch_url* url, etch_factory_params*, void* conximpl);
+
+etch_session* remove_etch_session (etch_server_factory*, const int session_id);
+int get_etch_session (etch_server_factory*, const int session_id, etch_session** out);
+int transport_teardown_client_sessions (i_sessionlistener*);
+int transport_session_count (i_sessionlistener*);
+int transport_thread_id (i_sessionlistener*);
+
+i_sessionlistener* new_etch_listener (wchar_t* uri, etch_resources* resources, void* factory_thisx, helper_listener_create_func, main_server_create_func, helper_resources_init_func);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHTRANSPORT_H */
diff --git a/binding-c/runtime/c/include/etch_transport_data.h b/binding-c/runtime/c/include/etch_transport_data.h
new file mode 100644
index 0000000..12f9694
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_transport_data.h
@@ -0,0 +1,83 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_transportdata.h
+ * i_transportdata interface
+ * interface used to deliver messages to the transport from the transport
+ */
+#ifndef ETCHITRANSPORTDATA_H
+#define ETCHITRANSPORTDATA_H
+
+#if(0)
+
+ TRANSPORTDATA
+ | void transportData(Who to, buffer)
+ - TRANSPORT<SessionData>
+ transportQuery(); transportl(); transport();
+ getSession(); setSession();
+
+#endif
+
+#include "etch_transportint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*etch_transport_data) (void* thisx, void* whofrom, void* buffer);
+
+
+/**
+ * i_transportdata
+ * transportdata interface
+ * a message handler delivers messages from a message source
+ */
+typedef struct i_transportdata
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+
+ /* transport interface */
+ i_transport* itransport; /* owned */
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ /**
+ * transport_data()
+ * delivers data to transport from ...
+ * @param to recipient
+ * @param message the message
+ * @return 0 success, -1 error
+ */
+ etch_transport_data transport_data;
+
+} i_transportdata;
+
+
+i_transportdata* new_transportdata_interface(void* thisx, etch_transport_data, i_transport*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHITRANSPORTDATA_H */
diff --git a/binding-c/runtime/c/include/etch_transport_message.h b/binding-c/runtime/c/include/etch_transport_message.h
new file mode 100644
index 0000000..f995d20
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_transport_message.h
@@ -0,0 +1,84 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_transportmsg.h
+ * i_transportmessage interface
+ * interface used to deliver messages to the transport from the transport
+ */
+
+#ifndef ETCHITRANSPORTMSG_H
+#define ETCHITRANSPORTMSG_H
+
+#if(0)
+
+ TRANSPORTMESSAGE
+ | void transportMessage(Who to, message)
+ - TRANSPORT<SessionMessage>
+ transportQuery(); transportl(); transport();
+ getSession(); setSession();
+
+#endif
+
+#include "etch_transportint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*etch_transport_message) (void* thisx, void* whofrom, void* message);
+
+
+/**
+ * i_transportmessage
+ * transportmessage interface
+ * a message handler delivers messages from a message source
+ */
+typedef struct i_transportmessage
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+
+ /* transport interface */
+ i_transport* itransport;
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ /**
+ * transport_message()
+ * delivers data to transport from ...
+ * @param to recipient
+ * @param message the message
+ * @return 0 success, -1 error
+ */
+ etch_transport_message transport_message;
+
+} i_transportmessage;
+
+
+i_transportmessage* new_transportmsg_interface(void* thisx, etch_transport_message, i_transport*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHITRANSPORTMSG_H */
diff --git a/binding-c/runtime/c/include/etch_transport_packet.h b/binding-c/runtime/c/include/etch_transport_packet.h
new file mode 100644
index 0000000..c029b4d
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_transport_packet.h
@@ -0,0 +1,93 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_transportpkt.h
+ * i_transportpacket interface
+ * interface used to deliver packets to transport from session
+ */
+
+#ifndef ETCHITRANSPORTPACKET_H
+#define ETCHITRANSPORTPACKET_H
+
+
+#if(0)
+
+ TRANSPORTPACKET
+ | int transportPacket(Who from, packetbuffer)
+ - TRANSPORT
+ transportQuery(); transportControl(); transportNotify();
+
+#endif
+
+
+#include "etch_transportint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*etch_transport_packet) (void* thisx, void* whoto, void* buffer);
+typedef int (*etch_transport_packet_headersize) (void* thisx);
+
+
+/**
+ * i_transportpacket
+ * transportpacket interface
+ */
+typedef struct i_transportpacket
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+
+ /* transport interface */
+ i_transport* itransport;
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ /**
+ * transport_packet()
+ * delivers packet to transport after adding packet header.
+ * @param to recipient
+ * @param buffer buffer positioned at the packet data, with space for header
+ * @return 0 success, -1 error
+ */
+ etch_transport_packet transport_packet;
+
+ /**
+ * etch_transport_packet_headersize()
+ * @return size of packet header in bytes
+ */
+ etch_transport_packet_headersize get_headersize;
+
+ int header_size;
+
+} i_transportpacket;
+
+
+i_transportpacket* new_transportpkt_interface(void* thisx, etch_transport_packet, i_transport*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHITRANSPORTPACKET_H */
diff --git a/binding-c/runtime/c/include/etch_transportint.h b/binding-c/runtime/c/include/etch_transportint.h
new file mode 100644
index 0000000..5b9e72c
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_transportint.h
@@ -0,0 +1,102 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_transportint.h
+ * transport interface
+ */
+#ifndef ETCHITRANSPORT_H
+#define ETCHITRANSPORT_H
+
+#include "etch_object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * the memory ownership contracts for control, notify, and query, are:
+ * caller relinquishes all arguments except of course the this pointer,
+ * regardless of outcome. it follows that only simple and temporal
+ * objects are passed as parameters.
+ */
+typedef int (*etch_transport_control) (void* thisx, etch_event* eventx, etch_object*);
+typedef int (*etch_transport_notify) (void* thisx, etch_event* eventx);
+typedef etch_object* (*etch_transport_query) (void* thisx, etch_query*);
+
+struct i_sessionmessage;
+typedef struct i_session* (*etch_transport_get_session) (void* thisx);
+typedef void (*etch_transport_set_session) (void* thisx, void*);
+
+
+/**
+ * i_transport
+ * transport interface
+ * not an etch object, ergo no destructor
+ */
+typedef struct i_transport
+{
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ void* thisx;
+
+} i_transport;
+
+
+
+/**
+ * i_transport_mask
+ * mask over any implemented transport interface
+ */
+typedef struct i_transport_mask
+{
+ etch_object object;
+
+ void* thisx; /* object which is the interface implementor */
+
+ /* transport interface */
+ i_transport* itransport;
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ void* main_transport_method;
+
+} i_transport_mask;
+
+
+i_transport* new_default_transport_interface();
+
+i_transport* new_transport_interface(void* thisx, etch_transport_control, etch_transport_notify, etch_transport_query);
+
+i_transport* new_transport_interface_ex(void* thisx, etch_transport_control, etch_transport_notify, etch_transport_query, etch_transport_get_session, etch_transport_set_session);
+
+i_transport* clone_transport(void* thisx, const i_transport*) ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ETCHITRANSPORT_H */
diff --git a/binding-c/runtime/c/include/etch_type.h b/binding-c/runtime/c/include/etch_type.h
new file mode 100644
index 0000000..f8aa3d1
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_type.h
@@ -0,0 +1,134 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_type.h
+ * an etch_type is an etch_id_name representing a type of a struct or message
+ */
+
+#ifndef ETCHTYPE_H
+#define ETCHTYPE_H
+
+#include "etch_id_name.h"
+#include "etch_hash.h"
+#include "etch_field.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct etch_serializer;
+
+#define ETCHTYPE_DEFSIZE_VTORMAP 4
+#define ETCHTYPE_DEFSIZE_FIELDMAP 8
+
+/**
+ * etch_type (see etch_id_name.h)
+ */
+typedef etch_id_name etch_type;
+
+struct etch_stub;
+struct i_delivery_service;
+struct etch_who;
+struct etch_message;
+
+/**
+ * nonspecific stub helper callback signature.
+ * the parameter types are in reality (StubBase, DeliveryService, T obj, Who, Message)
+ * however we can't include those headers here.
+ */
+typedef int (*opaque_stubhelper) (struct etch_stub* stub, struct i_delivery_service* delsvc, void* obj, struct etch_who* sender, struct etch_message* msg);
+
+/**
+ * etch_type_impl
+ * etch_type instance data extending etch_id_name
+ */
+typedef struct etch_type_impl
+{
+ etch_object object;
+
+ etch_hashtable* fieldmap; /* map owned, content not owned */
+ etch_hashtable* vtormap; /* map & keys owned, values maybe */
+ /* if validators order is significant we can use etch_arraylist */
+
+ etch_type* result_type; /* not owned */
+ etch_type* super_type; /* not owned */
+ etch_field* response_field; /* not owned */
+
+ opaque_stubhelper stubhelper; /* stub helper method */
+ struct etch_serializer* impexphelper; /* owned */
+
+ unsigned int timeout; /* ms to wait for response */
+ unsigned int component_class; /* type/class of content, not owned */
+ unsigned char is_run_validators;
+ unsigned char async_mode;
+
+} etch_type_impl;
+
+
+etch_type* new_type(const wchar_t* name);
+etch_type* new_static_type(const wchar_t* name);
+int destroy_static_type(etch_type*);
+
+etch_field* etchtype_add_field (etch_type*, etch_field* field);
+etch_field* etchtype_get_field_by_id (etch_type*, const unsigned id);
+etch_field* etchtype_get_field_by_name(etch_type*, const wchar_t* name);
+etch_id_name* etchtype_get_key_by_id (etch_hashtable*, const unsigned id);
+etch_id_name* etchtype_get_key_by_name (etch_hashtable*, const wchar_t*);
+int etchtype_set_fields_iterator(etch_type*, etch_iterator*);
+void* etchtype_get_fields (etch_type*);
+int etchtype_fields_count(etch_type*);
+
+int etchtype_put_validator (etch_type*, etch_field*, etch_object*);
+etch_object* etchtype_get_validator_by_id (etch_type*, const unsigned id);
+etch_object* etchtype_get_validator_by_name(etch_type*, const wchar_t* name);
+int etchtype_set_validators_iterator(etch_type*, etch_iterator*);
+int etchtype_clear_validator (etch_type*, etch_field*);
+int etchtype_clear_validators(etch_type*);
+int etchtype_validators_count(etch_type*);
+
+unsigned int etchtype_get_timeout(etch_type*);
+unsigned int etchtype_set_timeout(etch_type*, unsigned int ms);
+etch_field* etchtype_get_response_field(etch_type*);
+etch_field* etchtype_set_response_field(etch_type*, etch_field*);
+unsigned int etchtype_get_component_type(etch_type*);
+unsigned int etchtype_set_component_type(etch_type*, unsigned int typeclass);
+etch_type* etchtype_set_result_type(etch_type*, etch_type* rtype);
+etch_type* etchtype_get_result_type(etch_type*);
+etch_type* etchtype_set_super_type(etch_type*, etch_type* stype);
+etch_type* etchtype_get_super_type(etch_type*);
+unsigned char etchtype_set_run_validators(etch_type*, unsigned char val);
+unsigned char etchtype_get_run_validators(etch_type*);
+unsigned char etchtype_set_async_mode (etch_type*, unsigned char);
+unsigned char etchtype_get_async_mode (etch_type*);
+opaque_stubhelper etchtype_set_type_stubhelper(etch_type*, opaque_stubhelper);
+opaque_stubhelper etchtype_get_type_stubhelper(etch_type*);
+struct etch_serializer* etchtype_set_impexphelper(etch_type*, struct etch_serializer*);
+struct etch_serializer* etchtype_get_impexphelper(etch_type*);
+int etchtype_is_assignable_from(etch_type* type, etch_type* othertype);
+
+#define is_good_type is_good_id_name
+#define is_equal_types is_equal_id_names
+#define compute_type_id compute_id_name_id
+#define compute_type_id_from_widename compute_field_id_from_widename
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHTYPE_H*/
diff --git a/binding-c/runtime/c/include/etch_url.h b/binding-c/runtime/c/include/etch_url.h
new file mode 100644
index 0000000..ae4a861
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_url.h
@@ -0,0 +1,83 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etchurl.h -- URL class
+ * modeled after java binding URL class
+ */
+
+#ifndef ETCHURL_H
+#define ETCHURL_H
+
+#include "etch_collection.h"
+#include "etch_arraylist.h"
+#include "etch_hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCH_URL_DEFAULT_SCHEME L"http"
+#define ETCH_URL_DEFNUMPARMS 4
+#define ETCH_URL_DEFNUMTERMS 4
+#define ETCH_URL_DEFSUBTERMS 4
+
+
+typedef struct etch_url
+{
+ etch_object object;
+
+ unsigned port;
+
+ wchar_t* raw;
+ wchar_t* scheme;
+ wchar_t* user;
+ wchar_t* password;
+ wchar_t* fragment;
+ wchar_t* host;
+ wchar_t* uri;
+
+ etch_arraylist* params;
+ etch_hashtable* terms;
+
+ size_t charcount;
+ size_t bytecount;
+
+} etch_url;
+
+
+etch_url* new_url(wchar_t* urlstring);
+etch_object* etchurl_get_term (etch_url*, const wchar_t* termname);
+boolean etchurl_get_boolean_term(etch_url*, const wchar_t* termname, boolean* retval);
+etch_iterator* etchurl_get_params(etch_url*);
+etch_object* etchurl_remove_term(etch_url*, wchar_t* key);
+int etchurl_get_integer_term(etch_url*, const wchar_t* termname, int* retval);
+int etchurl_add_term(etch_url*, wchar_t* termname, wchar_t* termval);
+int etchurl_add_double_term( etch_url*, wchar_t* termname, const double termval);
+int etchurl_add_integer_term(etch_url*, wchar_t* termname, const int termval);
+int etchurl_paramcount(etch_url*);
+int etchurl_termcount (etch_url*);
+
+int is_url_scheme_http (etch_url*);
+int is_url_scheme_udp (etch_url*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCHURL_H */
diff --git a/binding-c/runtime/c/include/etch_validator.h b/binding-c/runtime/c/include/etch_validator.h
new file mode 100644
index 0000000..2f55072
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_validator.h
@@ -0,0 +1,265 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_validator.h
+ */
+
+#ifndef ETCH_VALIDATOR_H
+#define ETCH_VALIDATOR_H
+
+#include "etch_object.h"
+#include "etch_type.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ETCHVTOR_MAX_NDIMS 3 /* max array dimensions */
+#define ETCHVTOR_MAX_CACHED 4 /* max cached vtors per type */
+
+struct etch_validator;
+typedef int (*etchvtor_validate) (struct etch_validator*, etch_object*);
+typedef int (*etchvtor_checkvalue)(struct etch_validator*, etch_object*, byte* out);
+typedef etch_object* (*etchvtor_validate_value)(struct etch_validator*, etch_object*);
+typedef struct etch_validator* (*etchvtor_element_validator)(struct etch_validator*);
+
+
+/**
+ * etch_validator
+ * vet message values
+ */
+typedef struct etch_validator
+{
+ etch_object object;
+
+ /**
+ * etch_validator virtuals
+ */
+ etchvtor_validate validate;
+ etchvtor_checkvalue check_value;
+ etchvtor_validate_value validate_value;
+ etchvtor_element_validator element_validator;
+
+ /**
+ * etch_type_validator instance data
+ */
+ unsigned short expected_obj_type;
+ unsigned short expected_class_id;
+ int numdimensions;
+ char* description; /* owned */
+ etch_type* struct_type; /* not owned */
+ etchparentinfo* inherits_from; /* owned or not, see flag */
+ unsigned char is_cached;
+ unsigned char is_subclassable;
+ unsigned char is_owned_inherits_from;
+ unsigned char unused;
+
+ /* etch_combo_validator instance data
+ */
+ struct etch_validator* vtor_a; /* not owned */
+ struct etch_validator* vtor_b; /* not owned */
+
+} etch_validator;
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * validator constructors and public methods
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+etch_validator* new_validator();
+
+etch_validator* new_validator_from(etchvtor_validate, etchvtor_checkvalue, etchvtor_element_validator, etchvtor_validate_value);
+
+etch_validator* new_type_validator_1
+ (const unsigned short vtor_classid,
+ const unsigned short scalar_obj_type,
+ const unsigned short scalar_classid,
+ const unsigned short scalar_vtabkey,
+ const unsigned short array_classid,
+ const int ndims, char* descr);
+
+etch_validator* new_type_validator_2
+ (const unsigned short vtor_classid,
+ const unsigned short scalar_obj_type,
+ const unsigned short scalar_classid,
+ const unsigned short array_classid,
+ etchparentinfo* inheritlist,
+ const int ndims, char* descr);
+
+int etchvtor_check_dimensions(const int);
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * validator cache
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/* the validator cache is a single memory array, subdivided into caches for
+ * each validator type, which caches instantiated validators. validators are
+ * cached to a max number of dimensions. end of service processing should
+ * iterate the static cache and free memory for validators cached therein,
+ * in order for memory allocation tests to pass. validators have an is_cached
+ * flag, regarded by the etch_validator dtor, so validator user can destroy()
+ * validator regardless of its cache state, the dtor will check the flag and
+ * refuse to destroy if cached. the final cleanup must therefore clear the
+ * is_cached marker on each cached validator prior to calling destroy().
+ */
+#define ETCHVTOR_CACHED_TYPE_COUNT 12 /* count of cached validator types */
+#define ETCHVTOR_BYTES_PER_CACHE (sizeof(void*) * ETCHVTOR_MAX_CACHED)
+
+// the validator cache
+extern etch_validator* etchvtor_cache[ETCHVTOR_CACHED_TYPE_COUNT * ETCHVTOR_MAX_CACHED];
+
+void etchvtor_clear_cache();
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * boolean validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_BOOLEAN 0 /* boolean validator cache slot index */
+extern etch_validator** etchvtor_cache_boolean; /* boolean validator cache address */
+etch_validator* etchvtor_boolean_get(const int dimensions);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * byte validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_BYTE 1 /* byte validator cache slot index */
+extern etch_validator** etchvtor_cache_byte; /* byte validator cache address */
+etch_validator* etchvtor_byte_get(const int dimensions);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * int8 validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_INT8 2 /* int8 validator cache slot index */
+extern etch_validator** etchvtor_cache_int8; /* int8 validator cache address */
+etch_validator* etchvtor_int8_get(const int dimensions);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * int16 validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_INT16 3 /* int16 validator cache slot index */
+extern etch_validator** etchvtor_cache_int16; /* int16 validator cache address */
+etch_validator* etchvtor_int16_get(const int dimensions);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * int32 validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_INT32 4 /* int32 validator cache slot index */
+extern etch_validator** etchvtor_cache_int32; /* int32 validator cache address */
+etch_validator* etchvtor_int32_get(const int dimensions);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * int64 validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_INT64 5 /* int64 validator cache slot index */
+extern etch_validator** etchvtor_cache_int64; /* int64 validator cache address */
+etch_validator* etchvtor_int64_get(const int dimensions);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * float validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_FLOAT 6 /* float validator cache slot index */
+extern etch_validator** etchvtor_cache_float; /* float validator cache address */
+etch_validator* etchvtor_float_get (const int dimensions);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * double validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_DOUBLE 7 /* double validator cache slot index */
+extern etch_validator** etchvtor_cache_double; /* double validator cache address */
+etch_validator* etchvtor_double_get (const int dimensions);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * string validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_STRING 8 /* string validator cache slot index */
+extern etch_validator** etchvtor_cache_string; /* string validator cache address */
+etch_validator* etchvtor_string_get (const int dimensions);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * object validator (etch_object* anonymous wrapper)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_OBJECT 9 /* object validator cache slot index */
+extern etch_validator** etchvtor_cache_object; /* object validator cache address */
+etch_validator* etchvtor_object_get (const int dimensions);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * exception validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_EXCEPTION 10 /* excp validator cache slot index */
+extern etch_validator** etchvtor_cache_exception; /* excp validator cache address */
+etch_validator* etchvtor_exception_get();
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * eod_validator (end-of-data maker "none")
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+#define ETCHVTOR_CACHE_SLOT_EOD 11 /* eod validator cache slot index */
+extern etch_validator** etchvtor_cache_eod; /* eod validator cache address */
+etch_validator* etchvtor_eod_get();
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * struct validator (not cached since unique per type)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+etch_validator* new_validator_struct();
+etch_validator* etchvtor_struct_get(etch_type*, const int numdims);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * combo validator (not cached)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+etch_validator* new_combo_validator(etch_validator* vtor_a, etch_validator* vtor_b);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * custom validator (cached in a custom cache)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+etch_validator* new_validator_custom(const unsigned short obj_type, const unsigned short class_id, etch_type*, const int numdims);
+etch_validator* etchvtor_custom_get(const unsigned short obj_type, const unsigned short class_id, etch_type*, const int numdims);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_VALIDATOR_H */
diff --git a/binding-c/runtime/c/include/etch_value_factory.h b/binding-c/runtime/c/include/etch_value_factory.h
new file mode 100644
index 0000000..20b833e
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_value_factory.h
@@ -0,0 +1,199 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_valufact.h
+ * value factory is an interface defining the value factory which helps the
+ * idl compiler serialize and deserialize messages, convert values, etc.
+ */
+
+#ifndef ETCH_VALUFACT_H
+#define ETCH_VALUFACT_H
+
+#include "etch_msgutl.h"
+#include "etch_arraylist.h"
+#include "etch_structval.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct i_value_factory;
+struct etch_message;
+
+/**
+ * value_factory
+ * value factory implementation
+ */
+typedef struct etch_value_factory
+{
+ etch_object object;
+
+ etch_object* impl;
+
+} etch_value_factory;
+
+
+/**
+ * i_value_factory
+ * virtual function table for value factory
+ */
+struct i_value_factory
+{
+ etch_object object;
+
+ etchparentinfo* inherits_from;
+
+ /* - - - - - - - - - - - -
+ * type
+ * - - - - - - - - - - - -
+ */
+
+ /**
+ * adds a type to the set of types.
+ * @return the argument. If there is a collision with
+ * an id and name, both associated with the same type,
+ * then that type is returned instead of the argument.
+ * @throws IllegalArgumentException (returns NULL) if
+ * bad arg, or if collision in the id or name or both,
+ * when not associated with the same type.
+ */
+ etch_type* (*add_type) (void* vf, etch_type*);
+
+ /**
+ * translates a type id into the associated etch_type object.
+ */
+ etch_type* (*get_type_by_id) (void* vf, const unsigned id);
+
+ /**
+ * translates a name into the associated etch_type object.
+ */
+ etch_type* (*get_type_by_name) (void* vf, const wchar_t* name);
+
+ /**
+ * @return a collection of all the types
+ */
+ etch_arraylist* (*get_types) (void* vf);
+
+
+ /* - - - - - - - - - - - -
+ * encoding
+ * - - - - - - - - - - - -
+ */
+
+ /**
+ * @return the encoding to use for strings.
+ */
+ wchar_t* (*get_string_encoding) (void* vf);
+
+
+ /* - - - - - - - - - - - -
+ * message id
+ * - - - - - - - - - - - -
+ */
+
+ /**
+ * @param msg the message whose well-known id field is to be returned.
+ * @return the value of the well-known message-id field. This is a
+ * unique identifier for this message on a particular transport
+ * during a particular session. If there is no well-known message-id
+ * field defined, or if the message-id field has not been set, then
+ * return null. TODO: determine WHY we can't return an int64 with value
+ * zero equating to null?
+ */
+ etch_int64* (*get_message_id) (void* vf, struct etch_message*);
+
+ /**
+ * sets the value of the well-known message-id field. this is a unique
+ * identifier for this message on a particular transport during a
+ * particular session. if there is no well-known message-id field
+ * defined then no action is taken. if msgid is null, the field
+ * is cleared.
+ * regardless, the msgid object memory is owned by this method, to be
+ * assigned to the value factory; therefore if the action fails, this
+ * method must destroy the object.
+ */
+ int (*set_message_id) (void* vf, struct etch_message*, etch_int64* msgid);
+
+
+ /* - - - - - - - - - - - -
+ * reply to
+ * - - - - - - - - - - - -
+ */
+
+ /**
+ * @return the value of the in-reply-to field, or null if there is
+ * none or if there is no such field defined.
+ */
+ etch_int64* (*get_in_reply_to) (void* vf, struct etch_message*);
+
+ /**
+ * if no well-known in-reply-to field defined then no action is taken.
+ * if msgid is null, the field is cleared.
+ */
+ int (*set_in_reply_to) (void* vf, struct etch_message*, etch_int64* msgid);
+
+
+ /* - - - - - - - - - - - -
+ * value conversion
+ * - - - - - - - - - - - -
+ */
+
+ /**
+ * Converts a value to a struct value representation to be exported
+ * to a tagged data output.
+ * @param value a custom type defined by a service, or a well-known
+ * standard type (e.g., date).
+ * @return a struct value representing the value.
+ */
+ etch_structvalue* (*export_custom_value) (void* vf, etch_object* value);
+
+ /**
+ * Converts a struct value imported from a tagged data input to
+ * a normal type.
+ * @param sv a struct value representation of a custom type, or a
+ * well known standard type.
+ * @return a custom type, or a well known standard type.
+ */
+ etch_object* (*import_custom_value) (void* vf, etch_structvalue* sv);
+
+ /**
+ * @param class_id the class of a custom value.
+ * @return the struct type of a custom value class.
+ */
+ etch_type* (*get_custom_struct_type) (void* vf, const unsigned etchclass);
+
+ /**
+ * get well-known message type for exception thrown by oneway message
+ */
+ etch_type* (*get_mt_exception) (void* vf);
+};
+
+typedef struct i_value_factory i_value_factory;
+
+/**
+ * constructors/destructors
+ */
+etch_value_factory* new_value_factory(const int objlen);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_VALUFACT_H */
diff --git a/binding-c/runtime/c/include/etch_wait.h b/binding-c/runtime/c/include/etch_wait.h
new file mode 100644
index 0000000..45a0683
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_wait.h
@@ -0,0 +1,111 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_wait.h -- wait implementation
+ */
+
+#ifndef _ETCH_WAIT_H_
+#define _ETCH_WAIT_H_
+
+#include "etch_errno.h"
+#include "etch_mem.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct etch_wait_t etch_wait_t;
+
+/**
+ * create a new etch wait instance.
+ * @param waiter address where the new created mutex will be stored.
+ * @param pool memory pool, if pool is null the global pool will be used
+ * @return status
+ */
+etch_status_t etch_wait_create(etch_wait_t** waiter, etch_pool_t* pool);
+
+/**
+ * set autoreset mode
+ * @param waiter instance.
+ * @param autorest mode 0 = autoreset off, 1 = autoreset on. If autoreset is
+ * on, the signal state will automaticly reset after a signal was send to
+ * the sleeping threads.
+ * @return status
+ */
+
+
+/**
+ * wait until signaled.
+ * @param waiter instance
+ * @return status
+ */
+etch_status_t etch_wait_wait(etch_wait_t* waiter, int64 cond_value);
+
+/**
+ * wait until signaled and new value.
+ * @param waiter instance
+ * @param timeout in ms
+ * @return status
+ */
+etch_status_t etch_wait_wait_and_set(etch_wait_t* waiter, int64 cond_value, int64 new_cond_value);
+
+/**
+ * wait timed until signaled.
+ * @param waiter instance
+ * @param timeout in ms
+ * @return status
+ */
+etch_status_t etch_wait_timedwait(etch_wait_t* waiter, int64 cond_value, uint32 timeout);
+
+/**
+ * wait timed until signaled and new value.
+ * @param waiter instance
+ * @param timeout in ms
+ * @return status
+ */
+etch_status_t etch_wait_timedwait_and_set(etch_wait_t* waiter, int64 cond_value, uint32 timeout, int64 new_cond_value);
+
+
+/**
+ * set etch_wait in signaled state, all waiting thread will
+ * be signaled
+ * @param waiter instance
+ * @return status
+ */
+etch_status_t etch_wait_set(etch_wait_t* waiter, int64 cond_value);
+
+/**
+ * set condition variable without signaling waiting threads
+ * be signaled
+ * @param waiter instance
+ * @return status
+ */
+etch_status_t etch_wait_reset(etch_wait_t* waiter, int64 cond_value);
+
+/**
+ * destroy the given mutex.
+ * @param mutex
+ */
+etch_status_t etch_wait_destroy(etch_wait_t* waiter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ETCH_WAIT_H_ */
diff --git a/binding-c/runtime/c/include/etch_warn.h b/binding-c/runtime/c/include/etch_warn.h
new file mode 100644
index 0000000..23d1a89
--- /dev/null
+++ b/binding-c/runtime/c/include/etch_warn.h
@@ -0,0 +1,43 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_warn.h -- warn
+ */
+
+#ifndef ETCH_WARN_H
+#define ETCH_WARN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef _UNICODE
+#pragma message("WARNING: _UNICODE not defined")
+#endif
+
+#ifndef UNICODE
+#pragma message("WARNING: UNICODE not defined")
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef ETCH_WARN_H */
diff --git a/binding-c/runtime/c/include/etchinternal_single_linked_list.h b/binding-c/runtime/c/include/etchinternal_single_linked_list.h
new file mode 100644
index 0000000..4c060f6
--- /dev/null
+++ b/binding-c/runtime/c/include/etchinternal_single_linked_list.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef etch_thread_help_h_included
+#define etch_thread_help_h_included
+
+#include <stdlib.h>
+
+
+typedef struct etchinternal_single_linked_list_node {
+ void* data;
+ struct etchinternal_single_linked_list_node* next;
+} etchinternal_single_linked_list_node;
+
+typedef struct etchinternal_single_linked_list {
+ etchinternal_single_linked_list_node* head;
+} etchinternal_single_linked_list;
+
+etchinternal_single_linked_list_node* etchinternal_single_linked_list_node_create(void* data, etchinternal_single_linked_list_node* next);
+etchinternal_single_linked_list* etchinternal_single_linked_list_create();
+void etchinternal_single_linked_list_destroy(etchinternal_single_linked_list* list);
+void etchinternal_single_linked_list_add(etchinternal_single_linked_list* list, void* data, size_t size);
+
+typedef int (*etchinternal_find_func)(void*, void*);
+void* etchinternal_single_linked_list_find(etchinternal_single_linked_list* list, etchinternal_find_func find, void* find_data);
+
+#endif
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenkfasthash.c b/binding-c/runtime/c/src/extern/jenkhash/jenkfasthash.c
new file mode 100644
index 0000000..a74389e
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenkfasthash.c
@@ -0,0 +1,1008 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+-------------------------------------------------------------------------------
+lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+
+From website: "Update: I'm leaving the old hash ..., but I've got a new hash at
+http://burtleburtle.net/bob/c/lookup3.c (this code -- JLD) that is roughly twice
+as fast and more thorough than the one (used in lookup.c -- JLD). It's designed
+along the same lines as the hash below, 12 byte blocks, switch statements, etc.
+The biggest theoretical distinction is it has different mixing functions for the
+last block than for all but the last block, at 21 and 24 instructions, instead of
+the 36 instruction mix below that serves for both. It also has separate code paths
+for aligned and unaligned strings, to take advantage of 2-byte and 4-byte reads
+when it can. "
+
+These are functions for producing 32-bit hashes for hash table lookup.
+hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+are externally useful functions. Routines to test the hash are included
+if SELF_TEST is defined. You can use this free for any purpose. It's in
+the public domain. It has no warranty.
+
+You probably want to use hashlittle(). hashlittle() and hashbig()
+hash byte arrays. hashlittle() is is faster than hashbig() on
+little-endian machines. Intel and AMD are little-endian machines.
+On second thought, you probably want hashlittle2(), which is identical to
+hashlittle() except it returns two 32-bit hashes for the price of one.
+You could implement hashbig2() if you wanted but I haven't bothered here.
+
+If you want to find a hash of, say, exactly 7 integers, do
+ a = i1; b = i2; c = i3;
+ mix(a,b,c);
+ a += i4; b += i5; c += i6;
+ mix(a,b,c);
+ a += i7;
+ final(a,b,c);
+then use c as the hash value. If you have a variable length array of
+4-byte integers to hash, use hashword(). If you have a byte array (like
+a character string), use hashlittle(). If you have several byte arrays, or
+a mix of things, see the comments above hashlittle().
+
+Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
+then mix those integers. This is fast (you can do a lot more thorough
+mixing with 12*3 instructions on 3 integers than you can with 3 instructions
+on 1 byte), but shoehorning those bytes into integers efficiently is messy.
+-------------------------------------------------------------------------------
+*/
+#define SELF_TEST 1
+
+#include <stdio.h> /* defines printf for tests */
+#include <time.h> /* defines time_t for timings in the test */
+#include <stdint.h> /* defines uint32_t etc */
+#include <sys/param.h> /* attempt to define endianness */
+#ifdef linux
+# include <endian.h> /* attempt to define endianness */
+#endif
+
+/*
+ * My best guess at if you are big-endian or little-endian. This may
+ * need adjustment.
+ */
+#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ __BYTE_ORDER == __LITTLE_ENDIAN) || \
+ (defined(i386) || defined(__i386__) || defined(__i486__) || \
+ defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
+# define HASH_LITTLE_ENDIAN 1
+# define HASH_BIG_ENDIAN 0
+#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
+ __BYTE_ORDER == __BIG_ENDIAN) || \
+ (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 1
+#else
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 0
+#endif
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+-------------------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+
+This is reversible, so any information in (a,b,c) before mix() is
+still in (a,b,c) after mix().
+
+If four pairs of (a,b,c) inputs are run through mix(), or through
+mix() in reverse, there are at least 32 bits of the output that
+are sometimes the same for one pair and different for another pair.
+This was tested for:
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
+satisfy this are
+ 4 6 8 16 19 4
+ 9 15 3 18 27 15
+ 14 9 3 7 17 3
+Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+for "differ" defined as + with a one-bit base and a two-bit delta. I
+used http://burtleburtle.net/bob/hash/avalanche.html to choose
+the operations, constants, and arrangements of the variables.
+
+This does not achieve avalanche. There are input bits of (a,b,c)
+that fail to affect some output bits of (a,b,c), especially of a. The
+most thoroughly mixed value is c, but it doesn't really even achieve
+avalanche in c.
+
+This allows some parallelism. Read-after-writes are good at doubling
+the number of bits affected, so the goal of mixing pulls in the opposite
+direction as the goal of parallelism. I did what I could. Rotates
+seem to cost as much as shifts on every machine I could lay my hands
+on, and rotates are much kinder to the top and bottom bits, so I used
+rotates.
+-------------------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/*
+-------------------------------------------------------------------------------
+final -- final mixing of 3 32-bit values (a,b,c) into c
+
+Pairs of (a,b,c) values differing in only a few bits will usually
+produce values of c that look totally different. This was tested for
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+These constants passed:
+ 14 11 25 16 4 14 24
+ 12 14 25 16 4 14 24
+and these came close:
+ 4 8 15 26 3 22 24
+ 10 8 15 26 3 22 24
+ 11 8 15 26 3 22 24
+-------------------------------------------------------------------------------
+*/
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+/*
+--------------------------------------------------------------------
+ This works on all machines. To be useful, it requires
+ -- that the key be an array of uint32_t's, and
+ -- that the length be the number of uint32_t's in the key
+
+ The function hashword() is identical to hashlittle() on little-endian
+ machines, and identical to hashbig() on big-endian machines,
+ except that the length has to be measured in uint32_ts rather than in
+ bytes. hashlittle() is more complicated than hashword() only because
+ hashlittle() has to dance around fitting the key bytes into registers.
+--------------------------------------------------------------------
+*/
+uint32_t hashword(
+const uint32_t *k, /* the key, an array of uint32_t values */
+size_t length, /* the length of the key, in uint32_ts */
+uint32_t initval) /* the previous hash, or an arbitrary value */
+{
+ uint32_t a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;
+
+ /*------------------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*------------------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------------------------ report the result */
+ return c;
+}
+
+
+/*
+--------------------------------------------------------------------
+hashword2() -- same as hashword(), but take two seeds and return two
+32-bit values. pc and pb must both be nonnull, and *pc and *pb must
+both be initialized with seeds. If you pass in (*pb)==0, the output
+(*pc) will be the same as the return value from hashword().
+--------------------------------------------------------------------
+*/
+void hashword2 (
+const uint32_t *k, /* the key, an array of uint32_t values */
+size_t length, /* the length of the key, in uint32_ts */
+uint32_t *pc, /* IN: seed OUT: primary hash value */
+uint32_t *pb) /* IN: more seed OUT: secondary hash value */
+{
+ uint32_t a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc;
+ c += *pb;
+
+ /*------------------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*------------------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------------------------ report the result */
+ *pc=c; *pb=b;
+}
+
+
+/*
+-------------------------------------------------------------------------------
+hashlittle() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ length : the length of the key, counting by bytes
+ initval : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Two keys differing by one or two bits will have
+totally different hash values.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (uint8_t **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
+
+By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial. It's free.
+
+Use for hash table lookup, or anything where one collision in 2^^32 is
+acceptable. Do NOT use for cryptographic purposes.
+-------------------------------------------------------------------------------
+*/
+
+uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
+{
+ uint32_t a,b,c; /* internal state */
+ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+ const uint8_t *k8;
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff; break;
+ case 2 : a+=k[0]&0xffff; break;
+ case 1 : a+=k[0]&0xff; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : return c;
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : return c; /* zero length requires no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ return c;
+}
+
+
+/*
+ * hashlittle2: return 2 32-bit hash values
+ *
+ * This is identical to hashlittle(), except it returns two 32-bit hash
+ * values instead of just one. This is good enough for hash table
+ * lookup with 2^^64 buckets, or if you want a second hash if you're not
+ * happy with the first, or if you want a probably-unique 64-bit ID for
+ * the key. *pc is better mixed than *pb, so use *pc first. If you want
+ * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
+ */
+void hashlittle2(
+ const void *key, /* the key to hash */
+ size_t length, /* length of the key */
+ uint32_t *pc, /* IN: primary initval, OUT: primary hash */
+ uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */
+{
+ uint32_t a,b,c; /* internal state */
+ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc;
+ c += *pb;
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+ const uint8_t *k8;
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff; break;
+ case 2 : a+=k[0]&0xffff; break;
+ case 1 : a+=k[0]&0xff; break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+ }
+
+ final(a,b,c);
+ *pc=c; *pb=b;
+}
+
+
+
+/*
+ * hashbig():
+ * This is the same as hashword() on big-endian machines. It is different
+ * from hashlittle() on all machines. hashbig() takes advantage of
+ * big-endian byte ordering.
+ */
+uint32_t hashbig( const void *key, size_t length, uint32_t initval)
+{
+ uint32_t a,b,c;
+ union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
+
+ u.ptr = key;
+ if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+ const uint8_t *k8;
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]<<8" actually reads beyond the end of the string, but
+ * then shifts out the part it's not allowed to read. Because the
+ * string is aligned, the illegal read is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff00; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff0000; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff000000; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff00; break;
+ case 2 : a+=k[0]&0xffff0000; break;
+ case 1 : a+=k[0]&0xff000000; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<8; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<16; /* fall through */
+ case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */
+ case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */
+ case 1 : a+=((uint32_t)k8[0])<<24; break;
+ case 0 : return c;
+ }
+
+#endif /* !VALGRIND */
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += ((uint32_t)k[0])<<24;
+ a += ((uint32_t)k[1])<<16;
+ a += ((uint32_t)k[2])<<8;
+ a += ((uint32_t)k[3]);
+ b += ((uint32_t)k[4])<<24;
+ b += ((uint32_t)k[5])<<16;
+ b += ((uint32_t)k[6])<<8;
+ b += ((uint32_t)k[7]);
+ c += ((uint32_t)k[8])<<24;
+ c += ((uint32_t)k[9])<<16;
+ c += ((uint32_t)k[10])<<8;
+ c += ((uint32_t)k[11]);
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=k[11];
+ case 11: c+=((uint32_t)k[10])<<8;
+ case 10: c+=((uint32_t)k[9])<<16;
+ case 9 : c+=((uint32_t)k[8])<<24;
+ case 8 : b+=k[7];
+ case 7 : b+=((uint32_t)k[6])<<8;
+ case 6 : b+=((uint32_t)k[5])<<16;
+ case 5 : b+=((uint32_t)k[4])<<24;
+ case 4 : a+=k[3];
+ case 3 : a+=((uint32_t)k[2])<<8;
+ case 2 : a+=((uint32_t)k[1])<<16;
+ case 1 : a+=((uint32_t)k[0])<<24;
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ return c;
+}
+
+
+#ifdef SELF_TEST
+
+/* used for timings */
+void driver1()
+{
+ uint8_t buf[256];
+ uint32_t i;
+ uint32_t h=0;
+ time_t a,z;
+
+ time(&a);
+ for (i=0; i<256; ++i) buf[i] = 'x';
+ for (i=0; i<1; ++i)
+ {
+ h = hashlittle(&buf[0],1,h);
+ }
+ time(&z);
+ if (z-a > 0) printf("time %d %.8x\n", z-a, h);
+}
+
+/* check that every input bit changes every output bit half the time */
+#define HASHSTATE 1
+#define HASHLEN 1
+#define MAXPAIR 60
+#define MAXLEN 70
+void driver2()
+{
+ uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1];
+ uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z;
+ uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE];
+ uint32_t x[HASHSTATE],y[HASHSTATE];
+ uint32_t hlen;
+
+ printf("No more than %d trials should ever be needed \n",MAXPAIR/2);
+ for (hlen=0; hlen < MAXLEN; ++hlen)
+ {
+ z=0;
+ for (i=0; i<hlen; ++i) /*----------------------- for each input byte, */
+ {
+ for (j=0; j<8; ++j) /*------------------------ for each input bit, */
+ {
+ for (m=1; m<8; ++m) /*------------ for serveral possible initvals, */
+ {
+ for (l=0; l<HASHSTATE; ++l)
+ e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0);
+
+ /*---- check that every output bit is affected by that input bit */
+ for (k=0; k<MAXPAIR; k+=2)
+ {
+ uint32_t finished=1;
+ /* keys have one bit different */
+ for (l=0; l<hlen+1; ++l) {a[l] = b[l] = (uint8_t)0;}
+ /* have a and b be two keys differing in only one bit */
+ a[i] ^= (k<<j);
+ a[i] ^= (k>>(8-j));
+ c[0] = hashlittle(a, hlen, m);
+ b[i] ^= ((k+1)<<j);
+ b[i] ^= ((k+1)>>(8-j));
+ d[0] = hashlittle(b, hlen, m);
+ /* check every bit is 1, 0, set, and not set at least once */
+ for (l=0; l<HASHSTATE; ++l)
+ {
+ e[l] &= (c[l]^d[l]);
+ f[l] &= ~(c[l]^d[l]);
+ g[l] &= c[l];
+ h[l] &= ~c[l];
+ x[l] &= d[l];
+ y[l] &= ~d[l];
+ if (e[l]|f[l]|g[l]|h[l]|x[l]|y[l]) finished=0;
+ }
+ if (finished) break;
+ }
+ if (k>z) z=k;
+ if (k==MAXPAIR)
+ {
+ printf("Some bit didn't change: ");
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x ",
+ e[0],f[0],g[0],h[0],x[0],y[0]);
+ printf("i %d j %d m %d len %d\n", i, j, m, hlen);
+ }
+ if (z==MAXPAIR) goto done;
+ }
+ }
+ }
+ done:
+ if (z < MAXPAIR)
+ {
+ printf("Mix success %2d bytes %2d initvals ",i,m);
+ printf("required %d trials\n", z/2);
+ }
+ }
+ printf("\n");
+}
+
+/* Check for reading beyond the end of the buffer and alignment problems */
+void driver3()
+{
+ uint8_t buf[MAXLEN+20], *b;
+ uint32_t len;
+ uint8_t q[] = "This is the time for all good men to come to the aid of their country...";
+ uint32_t h;
+ uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country...";
+ uint32_t i;
+ uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country...";
+ uint32_t j;
+ uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country...";
+ uint32_t ref,x,y;
+ uint8_t *p;
+
+ printf("Endianness. These lines should all be the same (for values filled in):\n");
+ printf("%.8x %.8x %.8x\n",
+ hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13),
+ hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13),
+ hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13));
+ p = q;
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ p = &qq[1];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ p = &qqq[2];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ p = &qqqq[3];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ printf("\n");
+
+ /* check that hashlittle2 and hashlittle produce the same results */
+ i=47; j=0;
+ hashlittle2(q, sizeof(q), &i, &j);
+ if (hashlittle(q, sizeof(q), 47) != i)
+ printf("hashlittle2 and hashlittle mismatch\n");
+
+ /* check that hashword2 and hashword produce the same results */
+ len = 0xdeadbeef;
+ i=47, j=0;
+ hashword2(&len, 1, &i, &j);
+ if (hashword(&len, 1, 47) != i)
+ printf("hashword2 and hashword mismatch %x %x\n",
+ i, hashword(&len, 1, 47));
+
+ /* check hashlittle doesn't read before or after the ends of the string */
+ for (h=0, b=buf+1; h<8; ++h, ++b)
+ {
+ for (i=0; i<MAXLEN; ++i)
+ {
+ len = i;
+ for (j=0; j<i; ++j) *(b+j)=0;
+
+ /* these should all be equal */
+ ref = hashlittle(b, len, (uint32_t)1);
+ *(b+i)=(uint8_t)~0;
+ *(b-1)=(uint8_t)~0;
+ x = hashlittle(b, len, (uint32_t)1);
+ y = hashlittle(b, len, (uint32_t)1);
+ if ((ref != x) || (ref != y))
+ {
+ printf("alignment error: %.8x %.8x %.8x %d %d\n",ref,x,y,
+ h, i);
+ }
+ }
+ }
+}
+
+/* check for problems with nulls */
+ void driver4()
+{
+ uint8_t buf[1];
+ uint32_t h,i,state[HASHSTATE];
+
+
+ buf[0] = ~0;
+ for (i=0; i<HASHSTATE; ++i) state[i] = 1;
+ printf("These should all be different\n");
+ for (i=0, h=0; i<8; ++i)
+ {
+ h = hashlittle(buf, 0, h);
+ printf("%2ld 0-byte strings, hash is %.8x\n", i, h);
+ }
+}
+
+
+int main()
+{
+ driver1(); /* test that the key is hashed: used for timings */
+ driver2(); /* test that whole key is hashed thoroughly */
+ driver3(); /* test that nothing but the key is hashed */
+ driver4(); /* test hashing multiple buffers (all buffers are null) */
+ return 1;
+}
+
+#endif /* SELF_TEST */
+
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenkhash.sln b/binding-c/runtime/c/src/extern/jenkhash/jenkhash.sln
new file mode 100644
index 0000000..006f569
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenkhash.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jenkhash", "jenkhash.vcproj", "{CC75FED8-5D1B-4323-B73A-36C41FD1CC66}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Debug|Win32.Build.0 = Debug|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Release|Win32.ActiveCfg = Release|Win32
+ {CC75FED8-5D1B-4323-B73A-36C41FD1CC66}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenkhash.vcproj b/binding-c/runtime/c/src/extern/jenkhash/jenkhash.vcproj
new file mode 100644
index 0000000..f3b4205
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenkhash.vcproj
@@ -0,0 +1,469 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="jenkhash"
+ ProjectGUID="{CC75FED8-5D1B-4323-B73A-36C41FD1CC66}"
+ RootNamespace="jenkhash"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ProjectDir)target\win32\$(ConfigurationName)"
+ IntermediateDirectory="$(ProjectDir)target\win32\$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+ MinimalRebuild="false"
+ ExceptionHandling="0"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ AdditionalOptions="/NODEFAULTLIB:LIBCMT"
+ OutputFile="$(OutDir)\$(ProjectName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine=""
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Static|Win32"
+ OutputDirectory="$(ProjectDir)target\win32\$(ConfigurationName)"
+ IntermediateDirectory="$(ProjectDir)target\win32\$(ConfigurationName)"
+ ConfigurationType="4"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release Static|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ OutputDirectory="$(SolutionDir)Windows Mobile 5.0 Pocket PC SDK (ARMV4I)\$(ConfigurationName)"
+ IntermediateDirectory="Windows Mobile 5.0 Pocket PC SDK (ARMV4I)\$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="1"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;DEBUG;_LIB;APR_DECLARE_STATIC;API_DECLARE_STATIC;_CRT_SECURE_NO_WARNINGS;_WIN32_WCE=$(CEVER);UNDER_CE;WINCE;$(ARCHFAM);$(_ARCHFAM_)"
+ MinimalRebuild="false"
+ ExceptionHandling="0"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="1"
+ CompileAs="1"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ AdditionalOptions="/NODEFAULTLIB:LIBCMT"
+ OutputFile="..\lib\$(ProjectName).lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine=".\postbuild.bat"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"
+ OutputDirectory="$(SolutionDir)Windows Mobile 5.0 Pocket PC SDK (ARMV4I)\$(ConfigurationName)"
+ IntermediateDirectory="Windows Mobile 5.0 Pocket PC SDK (ARMV4I)\$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="1"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ ExecutionBucket="7"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCCodeSignTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ <DeploymentTool
+ ForceDirty="-1"
+ RemoteDirectory=""
+ RegisterOutput="0"
+ AdditionalFiles=""
+ />
+ <DebuggerTool
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\jenkhtab.c"
+ >
+ </File>
+ <File
+ RelativePath=".\jenklook.c"
+ >
+ </File>
+ <File
+ RelativePath=".\jenkrecy.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\jenkhtab.h"
+ >
+ </File>
+ <File
+ RelativePath=".\jenklook.h"
+ >
+ </File>
+ <File
+ RelativePath=".\jenkrecy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\jenkstd.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ <File
+ RelativePath=".\jenkmake.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\jenkread.txt"
+ >
+ </File>
+ <File
+ RelativePath=".\jenktest.txt"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenkhtab.c b/binding-c/runtime/c/src/extern/jenkhash/jenkhtab.c
new file mode 100644
index 0000000..753d5e4
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenkhtab.c
@@ -0,0 +1,463 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+--------------------------------------------------------------------
+By Bob Jenkins. hashtab.c. Public Domain.
+
+This implements a hash table.
+* Keys are unique. Adding an item fails if the key is already there.
+* Keys and items are pointed at, not copied. If you change the value
+ of the key after it is inserted then hfind will not be able to find it.
+* The hash table maintains a position that can be set and queried.
+* The table length doubles dynamically and never shrinks. The insert
+ that causes table doubling may take a long time.
+* The table length splits when the table length equals the number of items
+ Comparisons usually take 7 instructions.
+ Computing a hash value takes 35+6n instructions for an n-byte key.
+
+ hcreate - create a hash table
+ hdestroy - destroy a hash table
+ hcount - The number of items in the hash table
+ hkey - key at the current position
+ hkeyl - key length at the current position
+ hstuff - stuff at the current position
+ hfind - find an item in the table
+ hadd - insert an item into the table
+ hdel - delete an item from the table
+ hstat - print statistics about the table
+ hfirst - position at the first item in the table
+ hnext - move the position to the next item in the table
+--------------------------------------------------------------------
+*/
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "jenkstd.h"
+#include "jenklook.h"
+#include "jenkhtab.h"
+#include "jenkrecy.h"
+
+#define HSANITY 1
+
+/* sanity check -- make sure ipos, apos, and count make sense */
+static void hsanity(htab *t)
+{
+ ub4 i, end, counter;
+ hitem *h;
+
+ /* test that apos makes sense */
+ end = (ub4) 1 << (t->logsize);
+ if (end < t->apos)
+ printf("error: end %ld apos %ld\n", end, t->apos);
+
+ /* test that ipos is in bucket apos */
+ if (t->ipos)
+ {
+ for (h=t->table[t->apos]; h && h != t->ipos; h = h->next)
+ ;
+ if (h != t->ipos)
+ printf("error:ipos not in apos, apos is %ld\n", t->apos);
+ }
+
+ /* test that t->count is the number of elements in the table */
+ counter=0;
+ for (counter=0, i=0; i<end; ++i)
+ for (h=t->table[i]; h; h = h->next) ++counter;
+
+ if (counter != t->count)
+ printf("error: counter %ld t->count %ld\n", counter, t->count);
+}
+
+
+/*
+ * hgrow - Double the size of a hash table.
+ * Allocate a new, 2x bigger array,
+ * move everything from the old array to the new array, then free the old array.
+ */
+static void hgrow(htab *t)
+{
+ register ub4 newsize = (ub4)1<<(++t->logsize);
+ register ub4 newmask = newsize-1;
+ register ub4 i;
+ register hitem **oldtab = t->table;
+ register hitem **newtab = (hitem **) malloc(newsize*sizeof(hitem *));
+
+ /* make sure newtab is cleared */
+ for (i=0; i<newsize; ++i) newtab[i] = (hitem *)0;
+ t->table = newtab;
+ t->mask = newmask;
+
+ /* Walk through old table putting entries in new table */
+ for (i=newsize>>1; i--;)
+ {
+ register hitem *pthis, *pthat, **newplace;
+ for (pthis = oldtab[i]; pthis;)
+ {
+ pthat = pthis;
+ pthis = pthis->next;
+ newplace = &newtab[(pthat->hval & newmask)];
+ pthat->next = *newplace;
+ *newplace = pthat;
+ }
+ }
+
+ /* position the hash table on some existing item */
+ hfirst(t);
+
+ /* free the old array */
+ free((char*)oldtab);
+}
+
+
+/* hcreate - create a hash table initially of size power(2,logsize) */
+htab *hcreate(intx logsize)/* = log base 2 of the size of the hash table */
+{
+ ub4 i, len;
+ htab *t = (htab*) malloc(sizeof(htab));
+
+ len = ((ub4) 1 << logsize);
+ t->table = (hitem **) malloc(sizeof(hitem *)*(ub4)len);
+ for (i=0; i < len; ++i) t->table[i] = (hitem *)0;
+ t->logsize = logsize;
+ t->mask = len-1;
+ t->count = 0;
+ t->apos = (ub4)0;
+ t->ipos = (hitem *)0;
+ t->space = remkroot(sizeof(hitem));
+ t->bcount = 0;
+ return t;
+}
+
+
+/* hdestroy - destroy the hash table and free all its memory */
+void hdestroy(htab* t)
+{
+ /* hitem *h; // unreferenced local var wng */
+ refree(t->space);
+ free((char *)t->table);
+ free((char *)t);
+}
+
+
+/* hcount() is a macro, see hashtab.h */
+/* hkey() is a macro, see hashtab.h */
+/* hkeyl() is a macro, see hashtab.h */
+/* hstuff() is a macro, see hashtab.h */
+
+
+/**
+ * hfind - find an item with a given key in a hash table.
+ * if found, point at the item and return TRUE, otherwise FALSE.
+ */
+intx hfind(htab* t, ub1* key, ub4 keylen)
+{
+ hitem *h;
+ ub4 x = lookup(key,keylen,0); /* hash */
+ ub4 y;
+
+ for (h = t->table[y=(x&t->mask)]; h; h = h->next)
+ {
+ if ((x == h->hval) && (keylen == h->keyl) && !memcmp(key, h->key, keylen))
+ {
+ t->apos = y;
+ t->ipos = h;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/**
+ * hfindx - find an item with a given hashed key in a hash table.
+ * this function was added by JLD, Cisco CUAE.
+ * keylen is byte length of original key, not of the hash.
+ * if found, point at the item and return TRUE, otherwise FALSE.
+ */
+intx hfindx(htab* t, const ub4 hashval)
+{
+ intx result = FALSE;
+ ub4 i = hashval & t->mask;
+ hitem *h = t->table[i];
+
+ for (; h; h = h->next)
+ if (hashval == h->hval)
+ { t->apos = i;
+ t->ipos = h;
+ result = TRUE;
+ break;
+ }
+
+ return result;
+}
+
+
+
+/**
+ * hadd - add an item to a hash table.
+ * return FALSE if key is already in the table, otherwise TRUE.
+ */
+intx hadd(htab *t, ub1 *key, ub4 keylen, void* stuff)
+{
+ register hitem *h, **hp;
+ register ub4 y;
+ register ub4 x = lookup(key,keylen,0);
+
+ /* make sure the key is not already there */
+ for (h = t->table[(y = (x & t->mask))]; h; h = h->next)
+ {
+ if ((x == h->hval) && (keylen == h->keyl) && !memcmp(key, h->key, keylen))
+ {
+ t->apos = y;
+ t->ipos = h;
+ return FALSE;
+ }
+ }
+
+ /* find space for a new item */
+ /* JLD pointer assumed same size as int -- ouch! */
+ h = (hitem*) renew (t->space);
+
+ /* make the hash table bigger if it is getting full */
+ if (++t->count > (ub4) 1 << (t->logsize))
+ {
+ hgrow(t);
+ y = (x&t->mask);
+ }
+
+ /* add the new key to the table */
+ h->key = key;
+ h->keyl = keylen;
+ h->stuff = stuff;
+ h->hval = x; /* hash */
+ hp = &t->table[y];
+ h->next = *hp;
+ *hp = h;
+ t->ipos = h;
+ t->apos = y;
+
+ #ifdef HSANITY
+ hsanity(t);
+ #endif /* HSANITY */
+
+ return TRUE;
+}
+
+
+/**
+ * haddx - add an item to a hash table.
+ * this is a version of hadd which expects a pre-calculated hashed key
+ * in the initial 4 bytes of the key object.
+ * return FALSE if key is already in the table, otherwise TRUE.
+ * this function was added by JLD, Cisco CUAE
+ */
+intx haddx(htab *t, void* keyobj, void* valobj)
+{
+ register hitem *h, **hp;
+ register ub4 y;
+ register ub4 hashval = *((ub4*) keyobj);
+ const ub4 KEYLEN = sizeof(void*);
+ if (0 == hashval) return FALSE;
+
+ for (h = t->table[(y = (hashval & t->mask))]; h; h = h->next)
+ {
+ if ((hashval == h->hval) && (KEYLEN == h->keyl) && !memcmp(keyobj, h->key, KEYLEN))
+ {
+ t->apos = y;
+ t->ipos = h;
+ return FALSE;
+ }
+ }
+
+ /* find space for a new item */
+ h = (hitem*) renew (t->space);
+
+ /* make the hashval table bigger if it is getting full */
+ if (++t->count > (ub4) 1 << (t->logsize))
+ {
+ hgrow(t);
+ y = (hashval&t->mask);
+ }
+
+ /* add the new key and value to the table */
+ h->key = keyobj;
+ h->keyl = KEYLEN;
+ h->stuff = valobj;
+ h->hval = hashval;
+ hp = &t->table[y];
+ h->next = *hp;
+ *hp = h;
+ t->ipos = h;
+ t->apos = y;
+
+ #ifdef HSANITY
+ hsanity(t);
+ #endif /* HSANITY */
+
+ return TRUE;
+}
+
+
+/* hdel - delete the item at the current position */
+intx hdel(htab* t)
+{
+ hitem *h; /* item being deleted */
+ hitem **ip; /* a counter */
+
+ /* check for item not existing */
+ if (!(h = t->ipos)) return FALSE;
+
+ /* remove item from its list */
+ for (ip = &t->table[t->apos]; *ip != h; ip = &(*ip)->next)
+ ;
+ *ip = (*ip)->next;
+ --(t->count);
+
+ /* adjust position to something that exists */
+ if (!(t->ipos = h->next)) hnbucket(t);
+
+ /* recycle the deleted hitem node */
+ redel(t->space, h);
+
+ #ifdef HSANITY
+ hsanity(t);
+ #endif /* HSANITY */
+
+ return TRUE;
+}
+
+
+/* hfirst - position on the first element in the table */
+intx hfirst(htab *t)
+{
+ t->apos = (ub4) t->mask;
+ (void)hnbucket(t);
+ return (t->ipos != (hitem *)0);
+}
+
+
+/* hnext() is a macro, see hashtab.h */
+
+
+
+/*
+ * hnbucket - Move position to the first item in the next bucket.
+ * Return TRUE if we did not wrap around to the beginning of the table
+ */
+intx hnbucket(htab *t)
+{
+ ub4 oldapos = t->apos;
+ ub4 end = (ub4) 1 << (t->logsize);
+ ub4 i;
+
+ /* see if the element can be found without wrapping around */
+ for (i=oldapos+1; i<end; ++i)
+ {
+ if (t->table[i&t->mask])
+ {
+ t->apos = i;
+ t->ipos = t->table[i];
+ return TRUE;
+ }
+ }
+
+ /* must have to wrap around to find the last element */
+ for (i=0; i <= oldapos; ++i)
+ {
+ if (t->table[i])
+ {
+ t->apos = i;
+ t->ipos = t->table[i];
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ hstat: report table statistics
+*/
+void hstat(htab *t)
+{
+ ub4 i,j;
+ double total = 0.0;
+ hitem *h;
+ hitem *walk, *walk2, *stat = (hitem *)0;
+
+ /* in stat, keyl will store length of list, hval the number of buckets */
+ for (i=0; i <= t->mask; ++i)
+ {
+ for (h=t->table[i], j=0; h; ++j, h=h->next)
+ ;
+ for (walk=stat; walk && (walk->keyl != j); walk=walk->next)
+ ;
+ if (walk)
+ {
+ ++(walk->hval);
+ }
+ else
+ {
+ walk = (hitem *)renew(t->space);
+ walk->keyl = j;
+ walk->hval = 1;
+ if (!stat || stat->keyl > j) {walk->next=stat; stat=walk;}
+ else
+ {
+ for (walk2=stat;
+ walk2->next && (walk2->next->keyl<j);
+ walk2=walk2->next)
+ ;
+ walk->next = walk2->next;
+ walk2->next = walk;
+ }
+ }
+ }
+
+ /* figure out average list length for existing elements */
+ for (walk=stat; walk; walk=walk->next)
+ {
+ total += (double)walk->hval * (double)walk->keyl * (double)walk->keyl;
+ }
+
+ if (t->count)
+ total /= (double)t->count;
+ else total = 0.0;
+
+ /* print statistics */
+ printf("** hashtable stats follow\n");
+ for (walk=stat; walk; walk=walk->next)
+ printf("** items %ld: %ld buckets\n", walk->keyl, walk->hval);
+ printf("** buckets: %ld items: %ld existing: %g\n\n",
+ ((ub4)1<<t->logsize), t->count, total);
+
+ /* clean up */
+ while (stat)
+ {
+ walk = stat->next;
+ redel(t->space, stat);
+ stat = walk;
+ }
+}
+
+
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenkhtab.h b/binding-c/runtime/c/src/extern/jenkhash/jenkhtab.h
new file mode 100644
index 0000000..1db1075
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenkhtab.h
@@ -0,0 +1,233 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+--------------------------------------------------------------------
+By Bob Jenkins, 1996. hash.h. Public Domain.
+
+This implements a hash table.
+* Keys are unique. Adding an item fails if the key is already there.
+* Keys and items are pointed at, not copied. If you change the value
+ of the key after it is inserted then hfind will not be able to find it.
+* The hash table maintains a position that can be set and queried.
+* The table length doubles dynamically and never shrinks. The insert
+ that causes table doubling may take a long time.
+* The table length splits when the table length equals the number of items
+ Comparisons usually take 7 instructions.
+ Computing a hash value takes 35+6n instructions for an n-byte key.
+
+ hcreate - create a hash table
+ hdestroy - destroy a hash table
+ hcount - The number of items in the hash table
+ hkey - key at the current position
+ hkeyl - key length at the current position
+ hstuff - stuff at the current position
+ hfind - find an item in the table
+ hadd - insert an item into the table
+ hdel - delete an item from the table
+ hstat - print statistics about the table
+ hfirst - position at the first item in the table
+ hnext - move the position to the next item in the table
+--------------------------------------------------------------------
+*/
+
+#ifndef HASHTAB
+#define HASHTAB
+#include "jenkstd.h"
+
+#define HASHTAB_DEBUG /* #define HASHTAB_DEBUG to display debug info */
+
+/* PRIVATE TYPES AND DEFINITIONS */
+
+struct hitem
+{
+ ub1 *key; /* key that is hashed */
+ ub4 keyl; /* length of key */
+ void *stuff; /* stuff stored in this hitem */
+ ub4 hval; /* hash value */
+ struct hitem *next; /* next hitem in list */
+};
+typedef struct hitem hitem;
+
+
+struct htab
+{
+ struct hitem **table; /* hash table, array of size 2^logsize */
+ intx logsize; /* log of size of table */
+ size_t mask; /* (hashval & mask) is position in table */
+ ub4 count; /* how many items in this hash table so far? */
+ ub4 apos; /* position in the array */
+ struct hitem *ipos; /* current item in the array */
+ struct reroot *space; /* space for the hitems */
+ ub4 bcount; /* # hitems useable in current block */
+};
+typedef struct htab htab;
+
+
+/* PUBLIC FUNCTIONS */
+
+/* hcreate - create a hash table
+ ARGUMENTS:
+ logsize - 1<<logsize will be the initial table length
+ RETURNS:
+ the new table
+ */
+htab *hcreate(intx logsize);
+
+
+/* hdestroy - destroy a hash table
+ ARGUMENTS:
+ t - the hash table to be destroyed. Note that the items and keys
+ will not be freed, the user created them and must destroy
+ them himself.
+ RETURNS:
+ nothing
+ */
+void hdestroy(htab *t);
+
+
+/* hcount, hkey, hkeyl, hstuff
+ ARGUMENTS:
+ t - the hash table
+ RETURNS:
+ hcount - (ub4) The number of items in the hash table
+ hkey - (ub1 *) key for the current item
+ hkeyl - (ub4) key length for the current item
+ hstuff - (void *) stuff for the current item
+ NOTE:
+ The current position always has an item as long as there
+ are items in the table, so hexist can be used to test if the
+ table is empty.
+ hkey, hkeyl, and hstuff will crash if hcount returns 0
+ */
+#define hcount(t) ((t)->count)
+#define hkey(t) ((t)->ipos->key)
+#define hkeyl(t) ((t)->ipos->keyl)
+#define hstuff(t) ((t)->ipos->stuff)
+
+
+
+/* hfind - move the current position to a given key
+ ARGUMENTS:
+ t - the hash table
+ key - the key to look for
+ keyl - length of the key
+ RETURNS:
+ TRUE if the item exists, FALSE if it does not.
+ If the item exists, moves the current position to that item.
+ */
+intx hfind(htab *t, ub1 *key, ub4 keyl);
+
+intx hfindx(htab* t, const ub4 hashed);
+
+
+/* hadd - add a new item to the hash table
+ change the position to point at the item with the key
+ ARGUMENTS:
+ t - the hash table
+ key - the key to look for
+ keyl - length of the key
+ stuff - other stuff to be stored in this item
+ RETURNS:
+ FALSE if the operation fails (because that key is already there).
+ */
+intx hadd (htab *t, ub1 *key, ub4 keyl, void *stuff);
+
+intx haddx(htab *t, void *keyobj, void *stuff);
+
+
+/* hdel - delete the item at the current position
+ change the position to the following item
+ ARGUMENTS:
+ t - the hash table
+ RETURNS:
+ FALSE if there is no current item (meaning the table is empty)
+ NOTE:
+ This frees the item, but not the key or stuff stored in the item.
+ If you want these then deal with them first. For example:
+ if (hfind(tab, key, keyl))
+ {
+ free(hkey(tab));
+ free(hstuff(tab));
+ hdel(tab);
+ }
+ */
+intx hdel(htab *t);
+
+
+/* hfirst - move position to the first item in the table
+ ARGUMENTS:
+ t - the hash table
+ RETURNS:
+ FALSE if there is no current item (meaning the table is empty)
+ NOTE:
+ */
+intx hfirst(htab *t);
+
+
+/* hnext - move position to the next item in the table
+ ARGUMENTS:
+ t - the hash table
+ RETURNS:
+ FALSE if the position wraps around to the beginning of the table
+ NOTE:
+ To see every item in the table, do
+ if (hfirst(t)) do
+ {
+ key = hkey(t);
+ stuff = hstuff(t);
+ }
+ while (hnext(t));
+ */
+
+/* intx hnext(htab *t); */
+
+#define hnext(t) \
+ ((!(t)->ipos) ? FALSE : \
+ ((t)->ipos=(t)->ipos->next) ? TRUE : hnbucket(t))
+
+/* hnbucket - PRIVATE - move to first item in the next nonempty bucket
+ ARGUMENTS:
+ t - the hash table
+ RETURNS:
+ FALSE if the position wraps around to the beginning of the table
+ NOTE:
+ This is private to hashtab; do not use it externally.
+ */
+intx hnbucket(htab *t);
+
+
+/* hstat - print statistics about the hash table
+ ARGUMENTS:
+ t - the hash table
+ NOTE:
+ items <0>: <#buckets with zero items> buckets
+ items <1>: <#buckets with 1 item> buckets
+ ...
+ buckets: #buckets items: #items existing: x
+ ( x is the average length of the list when you look for an
+ item that exists. When the item does not exists, the average
+ length is #items/#buckets. )
+
+ If you put n items into n buckets, expect 1/(n!)e buckets to
+ have n items. That is, .3678 0, .3678 1, .1839 2, ...
+ Also expect "existing" to be about 2.
+ */
+void hstat(htab *t);
+
+#endif /* HASHTAB */
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenklook.c b/binding-c/runtime/c/src/extern/jenkhash/jenklook.c
new file mode 100644
index 0000000..878f266
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenklook.c
@@ -0,0 +1,262 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+--------------------------------------------------------------------
+lookupa.c, by Bob Jenkins, December 1996. Same as lookup2.c
+Use this code however you wish. Public Domain. No warranty.
+Source is http://burtleburtle.net/bob/c/lookupa.c
+--------------------------------------------------------------------
+*/
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "jenkstd.h"
+#include "jenklook.h"
+
+
+/*
+--------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+For every delta with one or two bit set, and the deltas of all three
+ high bits or all three low bits, whether the original value of a,b,c
+ is almost all zero or is uniformly distributed,
+* If mix() is run forward or backward, at least 32 bits in a,b,c
+ have at least 1/4 probability of changing.
+* If mix() is run forward, every bit of c will change between 1/3 and
+ 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
+mix() was built out of 36 single-cycle latency instructions in a
+ structure that could supported 2x parallelism, like so:
+ a -= b;
+ a -= c; x = (c>>13);
+ b -= c; a ^= x;
+ b -= a; x = (a<<8);
+ c -= a; b ^= x;
+ c -= b; x = (b>>13);
+ ...
+ Unfortunately, superscalar Pentiums and Sparcs can't take advantage
+ of that parallelism. They've also turned some of those single-cycle
+ latency instructions into multi-cycle latency instructions. Still,
+ this is the fastest good hash I could find. There were about 2^^68
+ to choose from. I only looked at a billion or so.
+--------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<<8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+}
+
+/*
+--------------------------------------------------------------------
+lookup() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ len : the length of the key, counting by bytes
+ level : the previous hash, or an arbitrary 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Every 1-bit and 2-bit delta achieves avalanche.
+About 6len+35 instructions.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (ub1 **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = lookup( k[i], len[i], h);
+
+By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial.
+
+See http://burtleburtle.net/bob/hash/evahash.html
+Use for hash table lookup, or anything where one collision in 2^32 is
+acceptable. Do NOT use for cryptographic purposes.
+--------------------------------------------------------------------
+*/
+
+ub4 lookup(register ub1 *k, register ub4 length, register ub4 level)
+{
+ register ub4 a,b,c,len;
+
+ /* Set up the internal state */
+ len = length;
+ a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+ c = level; /* the previous hash value */
+
+ /*---------------------------------------- handle most of the key */
+ while (len >= 12)
+ {
+ a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
+ b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
+ c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
+ mix(a,b,c);
+ k += 12; len -= 12;
+ }
+
+ /*------------------------------------- handle the last 11 bytes */
+ c += length;
+ switch(len) /* all the case statements fall through */
+ {
+ case 11: c+=((ub4)k[10]<<24);
+ case 10: c+=((ub4)k[9]<<16);
+ case 9 : c+=((ub4)k[8]<<8);
+ /* the first byte of c is reserved for the length */
+ case 8 : b+=((ub4)k[7]<<24);
+ case 7 : b+=((ub4)k[6]<<16);
+ case 6 : b+=((ub4)k[5]<<8);
+ case 5 : b+=k[4];
+ case 4 : a+=((ub4)k[3]<<24);
+ case 3 : a+=((ub4)k[2]<<16);
+ case 2 : a+=((ub4)k[1]<<8);
+ case 1 : a+=k[0];
+ /* case 0: nothing left to add */
+ }
+ mix(a,b,c);
+ /*-------------------------------------------- report the result */
+ return c;
+}
+
+
+/*
+--------------------------------------------------------------------
+mixc -- mixc 8 4-bit values as quickly and thoroughly as possible.
+Repeating mix() three times achieves avalanche.
+Repeating mix() four times eliminates all funnels and all
+ characteristics stronger than 2^{-11}.
+--------------------------------------------------------------------
+*/
+#define mixc(a,b,c,d,e,f,g,h) \
+{ \
+ a^=b<<11; d+=a; b+=c; \
+ b^=c>>2; e+=b; c+=d; \
+ c^=d<<8; f+=c; d+=e; \
+ d^=e>>16; g+=d; e+=f; \
+ e^=f<<10; h+=e; f+=g; \
+ f^=g>>4; a+=f; g+=h; \
+ g^=h<<8; b+=g; h+=a; \
+ h^=a>>9; c+=h; a+=b; \
+}
+
+/*
+--------------------------------------------------------------------
+checksum() -- hash a variable-length key into a 256-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ len : the length of the key, counting by bytes
+ state : an array of CHECKSTATE 4-byte values (256 bits)
+The state is the checksum. Every bit of the key affects every bit of
+the state. There are no funnels. About 112+6.875len instructions.
+
+If you are hashing n strings (ub1 **)k, do it like this:
+ for (i=0; i<8; ++i) state[i] = 0x9e3779b9;
+ for (i=0, h=0; i<n; ++i) checksum( k[i], len[i], state);
+
+(c) Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial, as long
+as this whole comment accompanies it.
+
+See http://burtleburtle.net/bob/hash/evahash.html
+Use to detect changes between revisions of documents, assuming nobody
+is trying to cause collisions. Do NOT use for cryptography.
+--------------------------------------------------------------------
+*/
+void checksum( k, len, state)
+register ub1 *k;
+register ub4 len;
+register ub4 *state;
+{
+ register ub4 a,b,c,d,e,f,g,h,length;
+
+ /* Use the length and level; add in the golden ratio. */
+ length = len;
+ a=state[0]; b=state[1]; c=state[2]; d=state[3];
+ e=state[4]; f=state[5]; g=state[6]; h=state[7];
+
+ /*---------------------------------------- handle most of the key */
+ while (len >= 32)
+ {
+ a += (k[0] +(k[1]<<8) +(k[2]<<16) +(k[3]<<24));
+ b += (k[4] +(k[5]<<8) +(k[6]<<16) +(k[7]<<24));
+ c += (k[8] +(k[9]<<8) +(k[10]<<16)+(k[11]<<24));
+ d += (k[12]+(k[13]<<8)+(k[14]<<16)+(k[15]<<24));
+ e += (k[16]+(k[17]<<8)+(k[18]<<16)+(k[19]<<24));
+ f += (k[20]+(k[21]<<8)+(k[22]<<16)+(k[23]<<24));
+ g += (k[24]+(k[25]<<8)+(k[26]<<16)+(k[27]<<24));
+ h += (k[28]+(k[29]<<8)+(k[30]<<16)+(k[31]<<24));
+ mixc(a,b,c,d,e,f,g,h);
+ mixc(a,b,c,d,e,f,g,h);
+ mixc(a,b,c,d,e,f,g,h);
+ mixc(a,b,c,d,e,f,g,h);
+ k += 32; len -= 32;
+ }
+
+ /*------------------------------------- handle the last 31 bytes */
+ h += length;
+ switch(len)
+ {
+ case 31: h+=(k[30]<<24);
+ case 30: h+=(k[29]<<16);
+ case 29: h+=(k[28]<<8);
+ case 28: g+=(k[27]<<24);
+ case 27: g+=(k[26]<<16);
+ case 26: g+=(k[25]<<8);
+ case 25: g+=k[24];
+ case 24: f+=(k[23]<<24);
+ case 23: f+=(k[22]<<16);
+ case 22: f+=(k[21]<<8);
+ case 21: f+=k[20];
+ case 20: e+=(k[19]<<24);
+ case 19: e+=(k[18]<<16);
+ case 18: e+=(k[17]<<8);
+ case 17: e+=k[16];
+ case 16: d+=(k[15]<<24);
+ case 15: d+=(k[14]<<16);
+ case 14: d+=(k[13]<<8);
+ case 13: d+=k[12];
+ case 12: c+=(k[11]<<24);
+ case 11: c+=(k[10]<<16);
+ case 10: c+=(k[9]<<8);
+ case 9 : c+=k[8];
+ case 8 : b+=(k[7]<<24);
+ case 7 : b+=(k[6]<<16);
+ case 6 : b+=(k[5]<<8);
+ case 5 : b+=k[4];
+ case 4 : a+=(k[3]<<24);
+ case 3 : a+=(k[2]<<16);
+ case 2 : a+=(k[1]<<8);
+ case 1 : a+=k[0];
+ }
+ mixc(a,b,c,d,e,f,g,h);
+ mixc(a,b,c,d,e,f,g,h);
+ mixc(a,b,c,d,e,f,g,h);
+ mixc(a,b,c,d,e,f,g,h);
+
+ /*-------------------------------------------- report the result */
+ state[0]=a; state[1]=b; state[2]=c; state[3]=d;
+ state[4]=e; state[5]=f; state[6]=g; state[7]=h;
+}
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenklook.h b/binding-c/runtime/c/src/extern/jenkhash/jenklook.h
new file mode 100644
index 0000000..b363815
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenklook.h
@@ -0,0 +1,42 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+------------------------------------------------------------------------------
+By Bob Jenkins, September 1996.
+lookupa.h, a hash function for table lookup, same function as lookup.c.
+Use this code in any way you wish. Public Domain. It has no warranty.
+Source is http://burtleburtle.net/bob/c/lookupa.h
+------------------------------------------------------------------------------
+*/
+
+#ifndef LOOKUPA
+#define LOOKUPA
+
+#ifndef STANDARD
+#include "jenkstd.h"
+#endif
+
+#define CHECKSTATE 8
+#define hashsize(n) ((ub4)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+
+ub4 lookup(ub1 *k, ub4 length, ub4 level);
+void checksum(ub1 *k, ub4 length, ub4 *state);
+
+#endif /* LOOKUPA */
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenkmake.txt b/binding-c/runtime/c/src/extern/jenkhash/jenkmake.txt
new file mode 100644
index 0000000..c23f5e4
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenkmake.txt
@@ -0,0 +1,19 @@
+CFLAGS = -O
+
+.cc.o:
+ gcc $(CFLAGS) -c $<
+
+O = recycle.o lookupa.o hashtab.o unique.o
+
+unique : $(O)
+ gcc -o unique $(O) -lm
+
+# DEPENDENCIES
+
+recycle.o : recycle.c standard.h recycle.h
+
+lookupa.o : lookupa.c standard.h lookupa.h
+
+hashtab.o : hashtab.c standard.h recycle.h lookupa.h hashtab.h
+
+unique.o : unique.c standard.h hashtab.h
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenkrecy.c b/binding-c/runtime/c/src/extern/jenkhash/jenkrecy.c
new file mode 100644
index 0000000..bb4dcb8
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenkrecy.c
@@ -0,0 +1,105 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+--------------------------------------------------------------------
+By Bob Jenkins, September 1996. recycle.c
+You may use this code in any way you wish, and it is free. No warranty.
+
+This manages memory for commonly-allocated structures.
+It allocates RESTART to REMAX items at a time.
+Timings have shown that, if malloc is used for every new structure,
+malloc will consume about 90% of the time in a program.
+This module cuts down the number of mallocs by an order of magnitude.
+This also decreases memory fragmentation, and freeing structures
+only requires freeing the root.
+--------------------------------------------------------------------
+*/
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "jenkstd.h"
+#include "jenkrecy.h"
+
+
+reroot *remkroot(size_t size)
+{
+ reroot *r = (reroot*) remalloc(sizeof(reroot), "recycle.c, root");
+ r->list = (recycle*)0;
+ r->trash = (recycle*)0;
+ r->size = align(size);
+ r->logsize = RESTART;
+ r->numleft = 0;
+ return r;
+}
+
+
+
+void refree(struct reroot *r)
+{
+ recycle *temp;
+ if (temp = r->list) while (r->list)
+ {
+ temp = r->list->next;
+ free((char *)r->list);
+ r->list = temp;
+ }
+ free((char *)r);
+ return;
+}
+
+
+
+/* to be called from the macro renew only */
+char *renewx(struct reroot *r)
+{
+ recycle *temp;
+ if (r->trash)
+ { /* pull a node off the trash heap */
+ temp = r->trash;
+ r->trash = temp->next;
+ (void)memset((void *)temp, 0, r->size);
+ }
+ else
+ { /* allocate a new block of nodes */
+ r->numleft = (int) r->size*((ub4)1<<r->logsize);
+ if (r->numleft < REMAX) ++r->logsize;
+ temp = (recycle *)remalloc(sizeof(recycle) + r->numleft,
+ "recycle.c, data");
+ temp->next = r->list;
+ r->list = temp;
+ r->numleft -= (int) r->size;
+ temp = (recycle *)((char *)(r->list+1)+r->numleft);
+ }
+ return (char *)temp;
+}
+
+
+char *remalloc(size_t len, char* purpose)
+{
+ char *x = (char*) malloc(len);
+ if (!x)
+ {
+ fprintf(stderr, "malloc %lu failed for %s\n", (unsigned long)len, purpose);
+ exit(SUCCESS);
+ }
+ return x;
+}
+
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenkrecy.h b/binding-c/runtime/c/src/extern/jenkhash/jenkrecy.h
new file mode 100644
index 0000000..1099f1c
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenkrecy.h
@@ -0,0 +1,82 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+--------------------------------------------------------------------
+By Bob Jenkins, September 1996. recycle.h
+You may use this code in any way you wish, and it is free. No warranty.
+
+This manages memory for commonly-allocated structures.
+It allocates RESTART to REMAX items at a time.
+Timings have shown that, if malloc is used for every new structure,
+ malloc will consume about 90% of the time in a program. This
+ module cuts down the number of mallocs by an order of magnitude.
+This also decreases memory fragmentation, and freeing all structures
+ only requires freeing the root.
+--------------------------------------------------------------------
+*/
+
+#include "jenkstd.h"
+
+#ifndef RECYCLE
+#define RECYCLE
+
+#define RESTART 0
+#define REMAX 32000
+
+struct recycle
+{
+ struct recycle *next;
+};
+typedef struct recycle recycle;
+
+struct reroot
+{
+ struct recycle *list; /* list of malloced blocks */
+ struct recycle *trash; /* list of deleted items */
+ size_t size; /* size of an item */
+ size_t logsize; /* log_2 of number of items in a block */
+ intx numleft; /* number of bytes left in this block */
+};
+typedef struct reroot reroot;
+
+/* make a new recycling root */
+reroot *remkroot(size_t mysize);
+
+/* free a recycling root and all the items it has made */
+void refree(struct reroot *r);
+
+/* get a new (cleared) item from the root */
+#define renew(r) ((r)->numleft ? \
+ (((char *)((r)->list+1))+((r)->numleft-=(int)(r)->size)) : renewx(r))
+
+char *renewx(struct reroot *r);
+
+
+/* delete an item; let the root recycle it */
+/* void redel(/o_ struct reroot *r, struct recycle *item _o/); */
+#define redel(root,item) { \
+ ((recycle *)item)->next=(root)->trash; \
+ (root)->trash=(recycle *)(item); \
+}
+
+/* malloc, but complain to stderr and exit program if no joy */
+/* use plain free() to free memory allocated by remalloc() */
+char *remalloc(size_t len, char *purpose);
+
+#endif /* RECYCLE */
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenkstd.h b/binding-c/runtime/c/src/extern/jenkhash/jenkstd.h
new file mode 100644
index 0000000..e032385
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenkstd.h
@@ -0,0 +1,110 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+------------------------------------------------------------------------------
+Standard definitions and types, Bob Jenkins
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+#define STANDARD
+
+/*
+ JLD cisco systems: ensure VS 2005 wide character support is turned on.
+ These definitions are enabled in the VS project, however for Linux and etc.
+ they may serve as a reminder that an etch binding should be unicode enabled.
+*/
+#ifndef _UNICODE /* defined by default in VS2005 */
+#define _UNICODE
+#endif
+
+#if defined(WIN32) && !defined(_WIN32_WCE)
+#include <tchar.h> /* wide char support, wmain() vs main() */
+#else
+#include <wchar.h>
+#endif
+
+#ifndef UNICODE /* defined by default in VS2005 */
+#define UNICODE
+#endif
+/*
+ end VS 2005 wide character support -- JLD
+*/
+
+#include <stdio.h>
+#include <stddef.h>
+
+typedef unsigned long long ub8;
+#define UB8MAXVAL 0xffffffffffffffffLL
+#define UB8BITS 64
+typedef signed long long sb8;
+#define SB8MAXVAL 0x7fffffffffffffffLL
+typedef unsigned long int ub4; /* unsigned 4-byte quantities */
+#define UB4MAXVAL 0xffffffff
+typedef signed long int sb4;
+#define UB4BITS 32
+#define SB4MAXVAL 0x7fffffff
+typedef unsigned short int ub2;
+#define UB2MAXVAL 0xffff
+#define UB2BITS 16
+typedef signed short int sb2;
+#define SB2MAXVAL 0x7fff
+typedef unsigned char ub1;
+#define UB1MAXVAL 0xff
+#define UB1BITS 8
+typedef signed char sb1; /* signed 1-byte quantities */
+#define SB1MAXVAL 0x7f
+
+/* JLD replaced Jenkins' 'word' typdef with 'intx'. 'word' is too
+ likely to be confused by the reader with a Windows 16-bit integer.
+ intx is brief, and is more readable as 'the register size on the
+ host OS". It remains unclear whether simply changing this typedef to
+ a 64-bit integer, on a 64-bit OS, is sufficient for the hashtable
+ to work as advertised in 64 bits. See macro renew() for example,
+ where it appears as if sizeof(char*) is assumed same as sizeof(int).
+*/
+typedef int intx;
+/* typedef int word fastest type available */
+
+#define bis(target,mask) ((target) |= (mask))
+#define bic(target,mask) ((target) &= ~(mask))
+#define bit(target,mask) ((target) & (mask))
+
+/* JLD commented this stuff out
+#ifndef min
+#define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a,b) (((a)<(b)) ? (b) : (a))
+#endif
+*/
+
+#ifndef align
+#define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1)))
+#endif
+
+#ifndef abs
+#define abs(a) (((a)>0) ? (a) : -(a))
+#endif
+
+#define TRUE 1
+#define FALSE 0
+#define SUCCESS 0 /* 1 on VAX */
+
+#endif /* STANDARD */
diff --git a/binding-c/runtime/c/src/extern/jenkhash/jenktest.txt b/binding-c/runtime/c/src/extern/jenkhash/jenktest.txt
new file mode 100644
index 0000000..1183597
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/jenktest.txt
@@ -0,0 +1,58 @@
+apple
+banana
+banter
+baseball
+blueberry
+brown
+browser
+camera
+can
+cantaloupe
+cherry
+cinnamon
+clear
+dash
+date
+distance
+ever
+ewe
+eye
+fig
+grape
+her
+lake
+leaf
+leave
+lemon
+lever
+lime
+mellon
+mango
+math
+music
+netscape
+never
+orange
+pear
+pepper
+politics
+psychedelic
+psychology
+salt
+science
+sift
+signals
+softball
+spice
+stupid
+swear
+syntax
+strawberry
+tangerine
+towel
+turn
+washcloth
+watermelon
+will
+words
+yellow
diff --git a/binding-c/runtime/c/src/extern/jenkhash/readme-jenk.txt b/binding-c/runtime/c/src/extern/jenkhash/readme-jenk.txt
new file mode 100644
index 0000000..b782a65
--- /dev/null
+++ b/binding-c/runtime/c/src/extern/jenkhash/readme-jenk.txt
@@ -0,0 +1,90 @@
+
+This is Bob Jenkins' hash table code from burtleburtle.net/bob/hash/hashtab.html.
+The author, Bob Jenkins, has placed this code in the public domain,
+He states as such in comments in the code itself. Regarding inclusion of the
+code in an Apache project, he states:
+ I do prefer that you keep a pointer back to me as the source, so I can
+ answer questions if any come up. I would object if someone claimed
+ they'd written it themselves. But other than that, there's no restrictions,
+ you can remove or rewrite any of the comments or code,
+ including the disclaimers.
+
+ James DeCocq (jadecocq) wrote:
+ > Hi Bob,
+ > Last year I used your excellent hash table C code in a project which
+ > is now to become an Apache open source project. What is the usual
+ > attribution and licensing disclaimer procedures when folks use your
+ > code in open source projects?
+
+CUSTOMIZED FOR ETCH
+The original code has been customized for use by etch. Changes are as follows:
+- renamed files belonging to this subproject to begin with "jenk".
+- changed name of unique.c to jenktest.c; changed this to read from file not stdin
+- added includes to .c files such that they compile individually
+
+BEGIN JENKINS COMMENTS ON THE CODE
+hashtab.h and hashtab.c form the hash table module.
+The files before those are the support files that I always use.
+The file after it (unique.c) is an example of how to use the hash table.
+(The program UNIQUE takes a file in STDIN and dumps the unique lines
+(duplicates removed) to STDOUT.
+It also shuffles the unique lines pseudorandomly.
+The sample input provided doesn't have any duplicate lines,
+so the output should be the same size as the input, but the lines will be shuffled.)
+
+The hash table has a fixed hash function, and its size is a power of two.
+It doubles in size when it needs to, and when it doubles, it doubles all at once.
+It never shrinks. Input keys and data are pointed to, not copied.
+Keys are unique.
+Collisions are handled by chaining.
+
+Functions are:
+hcreate - create a hash table
+hdestroy - destroy a hash table
+hcount - how many items are in the hash table?
+hkey - the key at the current position
+hkeyl - the length of the key at the current position
+hstuff - the other data at the current position
+hfind - position the hash table at some key
+hadd - add a new <key,stuff> pair to the table
+hdel - delete the item at the current position
+hfirst - position at the first item in the table
+hnext - move to the next item in the table
+hstat - print statistics about this table
+
+The most unusual thing about this module is that it maintains a current position.
+This means you can't have a dangling pointer into it.
+If you position on something, and then delete it, the position moves to another item.
+On the other hand, it also means it's hard to do nested loops over all the items in the table,
+since there can be only one position at a time.
+END JENKINS COMMENTS ON THE CODE
+
+
+JENKINS EMAIL RE 64-BIT COMPATIBILITY
+I don't have any. However, Thomas Wang came up with this one:
+
+public long hash64shift(long key)
+{
+ key = (~key) + (key << 21); // key = (key << 21) - key - 1;
+ key = key ^ (key >>> 24);
+ key = (key + (key << 3)) + (key << 8); // key * 265
+ key = key ^ (key >>> 14);
+ key = (key + (key << 2)) + (key << 4); // key * 21
+ key = key ^ (key >>> 28);
+ key = key + (key << 31);
+ return key;
+}
+
+I haven't tested it, but the functions of his that I have tested aren't bad, and the operations look about like what I'd expect is needed. My preliminary stabs at a 64-bit functions said 8 or 9 shifts are needed, he's got 10, but many are done in parallel, so it does look like a promising function.
+
+James DeCocq (jadecocq) wrote:
+> Hi Bob,
+>
+> Have you tried the code in 64 bits? I don't currently have the ability
+> to do so, but a glance at some of the code, specifically the renew()
+> macro, seems as if it might assume that a pointer is the same size as
+> an int. If it is the case that it makes assumptions as to 32 bits, are
+> there specific tweaks you may have made for 64 bits that you can share?
+END JENKINS EMAIL RE 64-BIT COMPATIBILITY
+
+
diff --git a/binding-c/runtime/c/src/main/apr/etch_threadpool_apr.c b/binding-c/runtime/c/src/main/apr/etch_threadpool_apr.c
new file mode 100644
index 0000000..75dc753
--- /dev/null
+++ b/binding-c/runtime/c/src/main/apr/etch_threadpool_apr.c
@@ -0,0 +1,1025 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+/*
+ * etch_threadpool_apr.c
+ * apache portable runtime threadpool code
+ */
+
+#include "etch.h"
+#include "etch_threadpool_apr.h"
+#include "apr_ring.h"
+#include "apr_thread_cond.h"
+
+#define TASK_PRIORITY_SEGS 4
+#define TASK_PRIORITY_SEG(x) (((x)->dispatch.priority & 0xFF) / 64)
+
+
+typedef struct apr_thread_pool_task
+{
+ APR_RING_ENTRY(apr_thread_pool_task) link;
+ apr_thread_start_t func;
+ void *param;
+ void *owner;
+ union
+ {
+ apr_byte_t priority;
+ apr_time_t time;
+ } dispatch;
+} apr_thread_pool_task_t;
+
+
+APR_RING_HEAD(apr_thread_pool_tasks, apr_thread_pool_task);
+
+
+struct apr_thread_list_elt
+{
+ APR_RING_ENTRY(apr_thread_list_elt) link;
+ apr_thread_t *thd;
+ volatile void *current_owner;
+ volatile enum { TH_RUN, TH_STOP, TH_PROBATION } state;
+};
+
+
+APR_RING_HEAD(apr_thread_list, apr_thread_list_elt);
+
+
+struct apr_thread_pool
+{
+ apr_pool_t *pool;
+ volatile apr_size_t thd_max;
+ volatile apr_size_t idle_max;
+ volatile apr_interval_time_t idle_wait;
+ volatile apr_size_t thd_cnt;
+ volatile apr_size_t idle_cnt;
+ volatile apr_size_t task_cnt;
+ volatile apr_size_t scheduled_task_cnt;
+ volatile apr_size_t threshold;
+ volatile apr_size_t tasks_run;
+ volatile apr_size_t tasks_high;
+ volatile apr_size_t thd_high;
+ volatile apr_size_t thd_timed_out;
+ struct apr_thread_pool_tasks *tasks;
+ struct apr_thread_pool_tasks *scheduled_tasks;
+ struct apr_thread_list *busy_thds;
+ struct apr_thread_list *idle_thds;
+ apr_thread_mutex_t *lock;
+ apr_thread_mutex_t *cond_lock;
+ apr_thread_cond_t *cond;
+ volatile int terminated;
+ struct apr_thread_pool_tasks *recycled_tasks;
+ struct apr_thread_list *recycled_thds;
+ apr_thread_pool_task_t *task_idx[TASK_PRIORITY_SEGS];
+};
+
+
+
+static apr_status_t thread_pool_construct(apr_thread_pool_t * me,
+ apr_size_t init_threads,
+ apr_size_t max_threads)
+{
+ apr_status_t rv;
+ int i;
+
+ me->thd_max = max_threads;
+ me->idle_max = init_threads;
+ me->threshold = init_threads / 2;
+ rv = apr_thread_mutex_create(&me->lock, APR_THREAD_MUTEX_NESTED,
+ me->pool);
+ if (APR_SUCCESS != rv) {
+ return rv;
+ }
+ rv = apr_thread_mutex_create(&me->cond_lock, APR_THREAD_MUTEX_UNNESTED,
+ me->pool);
+ if (APR_SUCCESS != rv) {
+ apr_thread_mutex_destroy(me->lock);
+ return rv;
+ }
+ rv = apr_thread_cond_create(&me->cond, me->pool);
+ if (APR_SUCCESS != rv) {
+ apr_thread_mutex_destroy(me->lock);
+ apr_thread_mutex_destroy(me->cond_lock);
+ return rv;
+ }
+ me->tasks = apr_palloc(me->pool, sizeof(*me->tasks));
+ if (!me->tasks) {
+ goto CATCH_ENOMEM;
+ }
+ APR_RING_INIT(me->tasks, apr_thread_pool_task, link);
+ me->scheduled_tasks = apr_palloc(me->pool, sizeof(*me->scheduled_tasks));
+ if (!me->scheduled_tasks) {
+ goto CATCH_ENOMEM;
+ }
+ APR_RING_INIT(me->scheduled_tasks, apr_thread_pool_task, link);
+ me->recycled_tasks = apr_palloc(me->pool, sizeof(*me->recycled_tasks));
+ if (!me->recycled_tasks) {
+ goto CATCH_ENOMEM;
+ }
+ APR_RING_INIT(me->recycled_tasks, apr_thread_pool_task, link);
+ me->busy_thds = apr_palloc(me->pool, sizeof(*me->busy_thds));
+ if (!me->busy_thds) {
+ goto CATCH_ENOMEM;
+ }
+ APR_RING_INIT(me->busy_thds, apr_thread_list_elt, link);
+ me->idle_thds = apr_palloc(me->pool, sizeof(*me->idle_thds));
+ if (!me->idle_thds) {
+ goto CATCH_ENOMEM;
+ }
+ APR_RING_INIT(me->idle_thds, apr_thread_list_elt, link);
+ me->recycled_thds = apr_palloc(me->pool, sizeof(*me->recycled_thds));
+ if (!me->recycled_thds) {
+ goto CATCH_ENOMEM;
+ }
+ APR_RING_INIT(me->recycled_thds, apr_thread_list_elt, link);
+ me->thd_cnt = me->idle_cnt = me->task_cnt = me->scheduled_task_cnt = 0;
+ me->tasks_run = me->tasks_high = me->thd_high = me->thd_timed_out = 0;
+ me->idle_wait = 0;
+ me->terminated = 0;
+ for (i = 0; i < TASK_PRIORITY_SEGS; i++) {
+ me->task_idx[i] = NULL;
+ }
+ goto FINAL_EXIT;
+ CATCH_ENOMEM:
+ rv = APR_ENOMEM;
+ apr_thread_mutex_destroy(me->lock);
+ apr_thread_mutex_destroy(me->cond_lock);
+ apr_thread_cond_destroy(me->cond);
+ FINAL_EXIT:
+ return rv;
+}
+
+/*
+ * NOTE: This function is not thread safe by itself. Caller should hold the lock
+ */
+static apr_thread_pool_task_t *pop_task(apr_thread_pool_t * me)
+{
+ apr_thread_pool_task_t *task = NULL;
+ int seg;
+
+ /* check for scheduled tasks */
+ if (me->scheduled_task_cnt > 0) {
+ task = APR_RING_FIRST(me->scheduled_tasks);
+ ETCH_ASSERT(task != NULL);
+ ETCH_ASSERT(task != APR_RING_SENTINEL(me->scheduled_tasks, apr_thread_pool_task, link));
+ /* if it's time */
+ if (task->dispatch.time <= apr_time_now()) {
+ --me->scheduled_task_cnt;
+ APR_RING_REMOVE(task, link);
+ return task;
+ }
+ }
+ /* check for normal tasks if we're not returning a scheduled task */
+ if (me->task_cnt == 0) {
+ return NULL;
+ }
+
+ task = APR_RING_FIRST(me->tasks);
+ assert(task != NULL);
+ assert(task != APR_RING_SENTINEL(me->tasks, apr_thread_pool_task, link));
+ --me->task_cnt;
+ seg = TASK_PRIORITY_SEG(task);
+ if (task == me->task_idx[seg]) {
+ me->task_idx[seg] = APR_RING_NEXT(task, link);
+ if (me->task_idx[seg] == APR_RING_SENTINEL(me->tasks,
+ apr_thread_pool_task, link)
+ || TASK_PRIORITY_SEG(me->task_idx[seg]) != seg) {
+ me->task_idx[seg] = NULL;
+ }
+ }
+ APR_RING_REMOVE(task, link);
+ return task;
+}
+
+
+static apr_interval_time_t waiting_time(apr_thread_pool_t * me)
+{
+ apr_thread_pool_task_t *task = NULL;
+
+ task = APR_RING_FIRST(me->scheduled_tasks);
+ assert(task != NULL);
+ assert(task !=
+ APR_RING_SENTINEL(me->scheduled_tasks, apr_thread_pool_task,
+ link));
+ return task->dispatch.time - apr_time_now();
+}
+
+
+
+/*
+ * NOTE: This function is not thread safe by itself. Caller should hold the lock
+ */
+static struct apr_thread_list_elt *elt_new(apr_thread_pool_t * me,
+ apr_thread_t * t)
+{
+ struct apr_thread_list_elt *elt;
+
+ if (APR_RING_EMPTY(me->recycled_thds, apr_thread_list_elt, link)) {
+ elt = apr_pcalloc(me->pool, sizeof(*elt));
+ if (NULL == elt) {
+ return NULL;
+ }
+ }
+ else {
+ elt = APR_RING_FIRST(me->recycled_thds);
+ APR_RING_REMOVE(elt, link);
+ }
+
+ APR_RING_ELEM_INIT(elt, link);
+ elt->thd = t;
+ elt->current_owner = NULL;
+ elt->state = TH_RUN;
+ return elt;
+}
+
+
+
+/*
+ * The worker thread function. Take a task from the queue and perform it if
+ * there is any. Otherwise, put itself into the idle thread list and waiting
+ * for signal to wake up.
+ * The thread terminate directly by detach and exit when it is asked to stop
+ * after finishing a task. Otherwise, the thread should be in idle thread list
+ * and should be joined.
+ */
+static void *APR_THREAD_FUNC thread_pool_func(apr_thread_t * t, void *param)
+{
+ apr_status_t rv = APR_SUCCESS;
+ apr_thread_pool_t *me = param;
+ apr_thread_pool_task_t *task = NULL;
+ apr_interval_time_t wait;
+ struct apr_thread_list_elt *elt;
+
+ apr_thread_mutex_lock(me->lock);
+ elt = elt_new(me, t);
+ if (!elt) {
+ apr_thread_mutex_unlock(me->lock);
+ apr_thread_exit(t, APR_ENOMEM);
+ }
+
+ while (!me->terminated && elt->state != TH_STOP) {
+ /* Test if not new element, it is awakened from idle */
+ if (APR_RING_NEXT(elt, link) != elt) {
+ --me->idle_cnt;
+ APR_RING_REMOVE(elt, link);
+ }
+
+ APR_RING_INSERT_TAIL(me->busy_thds, elt, apr_thread_list_elt, link);
+ task = pop_task(me);
+ while (NULL != task && !me->terminated) {
+ ++me->tasks_run;
+ elt->current_owner = task->owner;
+ apr_thread_mutex_unlock(me->lock);
+ apr_thread_data_set(task, "apr_thread_pool_task", NULL, t);
+ task->func(t, task->param);
+ apr_thread_mutex_lock(me->lock);
+ APR_RING_INSERT_TAIL(me->recycled_tasks, task,
+ apr_thread_pool_task, link);
+ elt->current_owner = NULL;
+ if (TH_STOP == elt->state) {
+ break;
+ }
+ task = pop_task(me);
+ }
+ assert(NULL == elt->current_owner);
+ if (TH_STOP != elt->state)
+ APR_RING_REMOVE(elt, link);
+
+ /* Test if a busy thread been asked to stop, which is not joinable */
+ if ((me->idle_cnt >= me->idle_max
+ && !(me->scheduled_task_cnt && 0 >= me->idle_max)
+ && !me->idle_wait)
+ || me->terminated || elt->state != TH_RUN) {
+ --me->thd_cnt;
+ if ((TH_PROBATION == elt->state) && me->idle_wait)
+ ++me->thd_timed_out;
+ APR_RING_INSERT_TAIL(me->recycled_thds, elt,
+ apr_thread_list_elt, link);
+ apr_thread_mutex_unlock(me->lock);
+ apr_thread_detach(t);
+ apr_thread_exit(t, APR_SUCCESS);
+ return NULL; /* should not be here, safe net */
+ }
+
+ /* busy thread become idle */
+ ++me->idle_cnt;
+ APR_RING_INSERT_TAIL(me->idle_thds, elt, apr_thread_list_elt, link);
+
+ /*
+ * If there is a scheduled task, always scheduled to perform that task.
+ * Since there is no guarantee that current idle threads are scheduled
+ * for next scheduled task.
+ */
+ if (me->scheduled_task_cnt)
+ wait = waiting_time(me);
+ else if (me->idle_cnt > me->idle_max) {
+ wait = me->idle_wait;
+ elt->state = TH_PROBATION;
+ }
+ else
+ wait = -1;
+
+ apr_thread_mutex_unlock(me->lock);
+ apr_thread_mutex_lock(me->cond_lock);
+ if (wait >= 0) {
+ rv = apr_thread_cond_timedwait(me->cond, me->cond_lock, wait);
+ }
+ else {
+ rv = apr_thread_cond_wait(me->cond, me->cond_lock);
+ }
+ apr_thread_mutex_unlock(me->cond_lock);
+ apr_thread_mutex_lock(me->lock);
+ }
+
+ /* idle thread been asked to stop, will be joined */
+ --me->thd_cnt;
+ apr_thread_mutex_unlock(me->lock);
+ apr_thread_exit(t, APR_SUCCESS);
+ return NULL; /* should not be here, safe net */
+}
+
+static apr_status_t thread_pool_cleanup(void *me)
+{
+ apr_thread_pool_t *_self = me;
+
+ _self->terminated = 1;
+ etch_apr_thread_pool_idle_max_set(_self, 0);
+ while (_self->thd_cnt) {
+ apr_sleep(20 * 1000); /* spin lock with 20 ms */
+ }
+ apr_thread_mutex_destroy(_self->lock);
+ apr_thread_mutex_destroy(_self->cond_lock);
+ apr_thread_cond_destroy(_self->cond);
+ return APR_SUCCESS;
+}
+
+
+
+apr_status_t etch_apr_thread_pool_create(apr_thread_pool_t ** me,
+ apr_size_t init_threads,
+ apr_size_t max_threads,
+ apr_pool_t * pool)
+{
+ apr_thread_t *t;
+ apr_status_t rv = APR_SUCCESS;
+
+ *me = apr_pcalloc(pool, sizeof(**me));
+ if (!*me) {
+ return APR_ENOMEM;
+ }
+
+ (*me)->pool = pool;
+
+ rv = thread_pool_construct(*me, init_threads, max_threads);
+ if (APR_SUCCESS != rv) {
+ *me = NULL;
+ return rv;
+ }
+ apr_pool_cleanup_register(pool, *me, thread_pool_cleanup,
+ apr_pool_cleanup_null);
+
+ while (init_threads) {
+ rv = apr_thread_create(&t, NULL, thread_pool_func, *me, (*me)->pool);
+ if (APR_SUCCESS != rv) {
+ break;
+ }
+ ++(*me)->thd_cnt;
+ if ((*me)->thd_cnt > (*me)->thd_high)
+ (*me)->thd_high = (*me)->thd_cnt;
+ --init_threads;
+ }
+
+ return rv;
+}
+
+
+
+apr_status_t etch_apr_thread_pool_destroy(apr_thread_pool_t * me)
+{
+ return apr_pool_cleanup_run(me->pool, me, thread_pool_cleanup);
+}
+
+
+
+/*
+ * NOTE: This function is not thread safe by itself. Caller should hold the lock
+ */
+static apr_thread_pool_task_t *task_new(apr_thread_pool_t * me,
+ apr_thread_start_t func,
+ void *param, apr_byte_t priority,
+ void *owner, apr_time_t time)
+{
+ apr_thread_pool_task_t *t;
+
+ if (APR_RING_EMPTY(me->recycled_tasks, apr_thread_pool_task, link)) {
+ t = apr_pcalloc(me->pool, sizeof(*t));
+ if (NULL == t) {
+ return NULL;
+ }
+ }
+ else {
+ t = APR_RING_FIRST(me->recycled_tasks);
+ APR_RING_REMOVE(t, link);
+ }
+
+ APR_RING_ELEM_INIT(t, link);
+ t->func = func;
+ t->param = param;
+ t->owner = owner;
+ if (time > 0) {
+ t->dispatch.time = apr_time_now() + time;
+ }
+ else {
+ t->dispatch.priority = priority;
+ }
+ return t;
+}
+
+
+
+/*
+ * Test it the task is the only one within the priority segment.
+ * If it is not, return the first element with same or lower priority.
+ * Otherwise, add the task into the queue and return NULL.
+ *
+ * NOTE: This function is not thread safe by itself. Caller should hold the lock
+ */
+static apr_thread_pool_task_t *add_if_empty(apr_thread_pool_t * me,
+ apr_thread_pool_task_t * const t)
+{
+ int seg;
+ int next;
+ apr_thread_pool_task_t *t_next;
+
+ seg = TASK_PRIORITY_SEG(t);
+ if (me->task_idx[seg]) {
+ assert(APR_RING_SENTINEL(me->tasks, apr_thread_pool_task, link) !=
+ me->task_idx[seg]);
+ t_next = me->task_idx[seg];
+ while (t_next->dispatch.priority > t->dispatch.priority) {
+ t_next = APR_RING_NEXT(t_next, link);
+ if (APR_RING_SENTINEL(me->tasks, apr_thread_pool_task, link) ==
+ t_next) {
+ return t_next;
+ }
+ }
+ return t_next;
+ }
+
+ for (next = seg - 1; next >= 0; next--) {
+ if (me->task_idx[next]) {
+ APR_RING_INSERT_BEFORE(me->task_idx[next], t, link);
+ break;
+ }
+ }
+ if (0 > next) {
+ APR_RING_INSERT_TAIL(me->tasks, t, apr_thread_pool_task, link);
+ }
+ me->task_idx[seg] = t;
+ return NULL;
+}
+
+
+/*
+ * schedule a task to run in "time" microseconds. Find the spot in the ring where
+ * the time fits. Adjust the short_time so the thread wakes up when the time is reached.
+ */
+static apr_status_t schedule_task(apr_thread_pool_t *me,
+ apr_thread_start_t func, void *param,
+ void *owner, apr_interval_time_t time)
+{
+ apr_thread_pool_task_t *t;
+ apr_thread_pool_task_t *t_loc;
+ apr_thread_t *thd;
+ apr_status_t rv = APR_SUCCESS;
+ apr_thread_mutex_lock(me->lock);
+
+ t = task_new(me, func, param, 0, owner, time);
+ if (NULL == t) {
+ apr_thread_mutex_unlock(me->lock);
+ return APR_ENOMEM;
+ }
+ t_loc = APR_RING_FIRST(me->scheduled_tasks);
+ while (NULL != t_loc) {
+ /* if the time is less than the entry insert ahead of it */
+ if (t->dispatch.time < t_loc->dispatch.time) {
+ ++me->scheduled_task_cnt;
+ APR_RING_INSERT_BEFORE(t_loc, t, link);
+ break;
+ }
+ else {
+ t_loc = APR_RING_NEXT(t_loc, link);
+ if (t_loc ==
+ APR_RING_SENTINEL(me->scheduled_tasks, apr_thread_pool_task,
+ link)) {
+ ++me->scheduled_task_cnt;
+ APR_RING_INSERT_TAIL(me->scheduled_tasks, t,
+ apr_thread_pool_task, link);
+ break;
+ }
+ }
+ }
+ /* there should be at least one thread for scheduled tasks */
+ if (0 == me->thd_cnt) {
+ rv = apr_thread_create(&thd, NULL, thread_pool_func, me, me->pool);
+ if (APR_SUCCESS == rv) {
+ ++me->thd_cnt;
+ if (me->thd_cnt > me->thd_high)
+ me->thd_high = me->thd_cnt;
+ }
+ }
+ apr_thread_mutex_unlock(me->lock);
+ apr_thread_mutex_lock(me->cond_lock);
+ apr_thread_cond_signal(me->cond);
+ apr_thread_mutex_unlock(me->cond_lock);
+ return rv;
+}
+
+
+
+static apr_status_t add_task(apr_thread_pool_t *me, apr_thread_start_t func,
+ void *param, apr_byte_t priority, int push,
+ void *owner)
+{
+ apr_thread_pool_task_t *t;
+ apr_thread_pool_task_t *t_loc;
+ apr_thread_t *thd;
+ apr_status_t rv = APR_SUCCESS;
+
+ apr_thread_mutex_lock(me->lock);
+
+ t = task_new(me, func, param, priority, owner, 0);
+ if (NULL == t) {
+ apr_thread_mutex_unlock(me->lock);
+ return APR_ENOMEM;
+ }
+
+ t_loc = add_if_empty(me, t);
+ if (NULL == t_loc) {
+ goto FINAL_EXIT;
+ }
+
+ if (push) {
+ while (APR_RING_SENTINEL(me->tasks, apr_thread_pool_task, link) !=
+ t_loc && t_loc->dispatch.priority >= t->dispatch.priority) {
+ t_loc = APR_RING_NEXT(t_loc, link);
+ }
+ }
+ APR_RING_INSERT_BEFORE(t_loc, t, link);
+ if (!push) {
+ if (t_loc == me->task_idx[TASK_PRIORITY_SEG(t)]) {
+ me->task_idx[TASK_PRIORITY_SEG(t)] = t;
+ }
+ }
+
+ FINAL_EXIT:
+ me->task_cnt++;
+ if (me->task_cnt > me->tasks_high)
+ me->tasks_high = me->task_cnt;
+ if (0 == me->thd_cnt || (0 == me->idle_cnt && me->thd_cnt < me->thd_max &&
+ me->task_cnt > me->threshold)) {
+ rv = apr_thread_create(&thd, NULL, thread_pool_func, me, me->pool);
+ if (APR_SUCCESS == rv) {
+ ++me->thd_cnt;
+ if (me->thd_cnt > me->thd_high)
+ me->thd_high = me->thd_cnt;
+ }
+ }
+ apr_thread_mutex_unlock(me->lock);
+
+ apr_thread_mutex_lock(me->cond_lock);
+ apr_thread_cond_signal(me->cond);
+ apr_thread_mutex_unlock(me->cond_lock);
+
+ return rv;
+}
+
+
+
+apr_status_t etch_apr_thread_pool_push(apr_thread_pool_t *me,
+ apr_thread_start_t func,
+ void *param,
+ apr_byte_t priority,
+ void *owner)
+{
+ return add_task(me, func, param, priority, 1, owner);
+}
+
+
+
+apr_status_t etch_apr_thread_pool_schedule(apr_thread_pool_t *me,
+ apr_thread_start_t func,
+ void *param,
+ apr_interval_time_t time,
+ void *owner)
+{
+ return schedule_task(me, func, param, owner, time);
+}
+
+
+
+apr_status_t etch_apr_thread_pool_top(apr_thread_pool_t *me,
+ apr_thread_start_t func,
+ void *param,
+ apr_byte_t priority,
+ void *owner)
+{
+ return add_task(me, func, param, priority, 0, owner);
+}
+
+
+
+static apr_status_t remove_scheduled_tasks(apr_thread_pool_t *me,
+ void *owner)
+{
+ apr_thread_pool_task_t *t_loc;
+ apr_thread_pool_task_t *next;
+
+ t_loc = APR_RING_FIRST(me->scheduled_tasks);
+ while (t_loc !=
+ APR_RING_SENTINEL(me->scheduled_tasks, apr_thread_pool_task,
+ link)) {
+ next = APR_RING_NEXT(t_loc, link);
+ /* if this is the owner remove it */
+ if (t_loc->owner == owner) {
+ --me->scheduled_task_cnt;
+ APR_RING_REMOVE(t_loc, link);
+ }
+ t_loc = next;
+ }
+ return APR_SUCCESS;
+}
+
+
+
+static apr_status_t remove_tasks(apr_thread_pool_t *me, void *owner)
+{
+ apr_thread_pool_task_t *t_loc;
+ apr_thread_pool_task_t *next;
+ int seg;
+
+ t_loc = APR_RING_FIRST(me->tasks);
+ while (t_loc != APR_RING_SENTINEL(me->tasks, apr_thread_pool_task, link)) {
+ next = APR_RING_NEXT(t_loc, link);
+ if (t_loc->owner == owner) {
+ --me->task_cnt;
+ seg = TASK_PRIORITY_SEG(t_loc);
+ if (t_loc == me->task_idx[seg]) {
+ me->task_idx[seg] = APR_RING_NEXT(t_loc, link);
+ if (me->task_idx[seg] == APR_RING_SENTINEL(me->tasks,
+ apr_thread_pool_task,
+ link)
+ || TASK_PRIORITY_SEG(me->task_idx[seg]) != seg) {
+ me->task_idx[seg] = NULL;
+ }
+ }
+ APR_RING_REMOVE(t_loc, link);
+ }
+ t_loc = next;
+ }
+ return APR_SUCCESS;
+}
+
+
+
+static void wait_on_busy_threads(apr_thread_pool_t *me, void *owner)
+{
+#ifndef NDEBUG
+ apr_os_thread_t *os_thread;
+#endif
+ struct apr_thread_list_elt *elt;
+ apr_thread_mutex_lock(me->lock);
+ elt = APR_RING_FIRST(me->busy_thds);
+ while (elt != APR_RING_SENTINEL(me->busy_thds, apr_thread_list_elt, link)) {
+ if (elt->current_owner != owner) {
+ elt = APR_RING_NEXT(elt, link);
+ continue;
+ }
+#ifndef NDEBUG
+ /* make sure the thread is not the one calling tasks_cancel */
+ apr_os_thread_get(&os_thread, elt->thd);
+#ifdef WIN32
+ /* hack for apr win32 bug */
+ assert(!apr_os_thread_equal(apr_os_thread_current(), os_thread));
+#else
+ assert(!apr_os_thread_equal(apr_os_thread_current(), *os_thread));
+#endif
+#endif
+ while (elt->current_owner == owner) {
+ apr_thread_mutex_unlock(me->lock);
+ apr_sleep(200 * 1000);
+ apr_thread_mutex_lock(me->lock);
+ }
+ elt = APR_RING_FIRST(me->busy_thds);
+ }
+ apr_thread_mutex_unlock(me->lock);
+ return;
+}
+
+
+
+apr_status_t etch_apr_thread_pool_tasks_cancel(apr_thread_pool_t *me,
+ void *owner)
+{
+ apr_status_t rv = APR_SUCCESS;
+
+ apr_thread_mutex_lock(me->lock);
+ if (me->task_cnt > 0) {
+ rv = remove_tasks(me, owner);
+ }
+ if (me->scheduled_task_cnt > 0) {
+ rv = remove_scheduled_tasks(me, owner);
+ }
+ apr_thread_mutex_unlock(me->lock);
+ wait_on_busy_threads(me, owner);
+
+ return rv;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_tasks_count(apr_thread_pool_t *me)
+{
+ return me->task_cnt;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_scheduled_tasks_count(apr_thread_pool_t *me)
+{
+ return me->scheduled_task_cnt;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_threads_count(apr_thread_pool_t *me)
+{
+ return me->thd_cnt;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_busy_count(apr_thread_pool_t *me)
+{
+ return me->thd_cnt - me->idle_cnt;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_idle_count(apr_thread_pool_t *me)
+{
+ return me->idle_cnt;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_tasks_run_count(apr_thread_pool_t * me)
+{
+ return me->tasks_run;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_tasks_high_count(apr_thread_pool_t * me)
+{
+ return me->tasks_high;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_threads_high_count(apr_thread_pool_t * me)
+{
+ return me->thd_high;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_threads_idle_timeout_count(apr_thread_pool_t * me)
+{
+ return me->thd_timed_out;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_idle_max_get(apr_thread_pool_t *me)
+{
+ return me->idle_max;
+}
+
+
+apr_interval_time_t etch_apr_thread_pool_idle_wait_get(apr_thread_pool_t * me)
+{
+ return me->idle_wait;
+}
+
+
+
+/*
+ * This function stop extra idle threads to the cnt.
+ * @return the number of threads stopped
+ * NOTE: There could be busy threads become idle during this function
+ */
+static struct apr_thread_list_elt *trim_threads(apr_thread_pool_t *me,
+ apr_size_t *cnt, int idle)
+{
+ struct apr_thread_list *thds;
+ apr_size_t n, n_dbg, i;
+ struct apr_thread_list_elt *head, *tail, *elt;
+
+ apr_thread_mutex_lock(me->lock);
+ if (idle) {
+ thds = me->idle_thds;
+ n = me->idle_cnt;
+ }
+ else {
+ thds = me->busy_thds;
+ n = me->thd_cnt - me->idle_cnt;
+ }
+ if (n <= *cnt) {
+ apr_thread_mutex_unlock(me->lock);
+ *cnt = 0;
+ return NULL;
+ }
+ n -= *cnt;
+
+ head = APR_RING_FIRST(thds);
+ for (i = 0; i < *cnt; i++) {
+ head = APR_RING_NEXT(head, link);
+ }
+ tail = APR_RING_LAST(thds);
+ if (idle) {
+ APR_RING_UNSPLICE(head, tail, link);
+ me->idle_cnt = *cnt;
+ }
+
+ n_dbg = 0;
+ for (elt = head; elt != tail; elt = APR_RING_NEXT(elt, link)) {
+ elt->state = TH_STOP;
+ n_dbg++;
+ }
+ elt->state = TH_STOP;
+ n_dbg++;
+ assert(n == n_dbg);
+ *cnt = n;
+
+ apr_thread_mutex_unlock(me->lock);
+
+ APR_RING_PREV(head, link) = NULL;
+ APR_RING_NEXT(tail, link) = NULL;
+ return head;
+}
+
+
+
+static apr_size_t trim_idle_threads(apr_thread_pool_t *me, apr_size_t cnt)
+{
+ apr_size_t n_dbg;
+ struct apr_thread_list_elt *elt, *head, *tail;
+ apr_status_t rv;
+
+ elt = trim_threads(me, &cnt, 1);
+
+ apr_thread_mutex_lock(me->cond_lock);
+ apr_thread_cond_broadcast(me->cond);
+ apr_thread_mutex_unlock(me->cond_lock);
+
+ n_dbg = 0;
+ if (NULL != (head = elt)) {
+ while (elt) {
+ tail = elt;
+ apr_thread_join(&rv, elt->thd);
+ elt = APR_RING_NEXT(elt, link);
+ ++n_dbg;
+ }
+ apr_thread_mutex_lock(me->lock);
+ APR_RING_SPLICE_TAIL(me->recycled_thds, head, tail,
+ apr_thread_list_elt, link);
+ apr_thread_mutex_unlock(me->lock);
+ }
+ assert(cnt == n_dbg);
+
+ return cnt;
+}
+
+
+/* don't join on busy threads for performance reasons, who knows how long will
+ * the task takes to perform
+ */
+static apr_size_t trim_busy_threads(apr_thread_pool_t *me, apr_size_t cnt)
+{
+ trim_threads(me, &cnt, 0);
+ return cnt;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_idle_max_set(apr_thread_pool_t *me,
+ apr_size_t cnt)
+{
+ me->idle_max = cnt;
+ cnt = trim_idle_threads(me, cnt);
+ return cnt;
+}
+
+
+
+apr_interval_time_t etch_apr_thread_pool_idle_wait_set(apr_thread_pool_t * me,
+ apr_interval_time_t timeout)
+{
+ apr_interval_time_t oldtime;
+
+ oldtime = me->idle_wait;
+ me->idle_wait = timeout;
+
+ return oldtime;
+}
+
+
+
+apr_size_t etch_apr_thread_pool_thread_max_get(apr_thread_pool_t *me)
+{
+ return me->thd_max;
+}
+
+
+/*
+ * This function stop extra working threads to the new limit.
+ * NOTE: There could be busy threads become idle during this function
+ */
+apr_size_t etch_apr_thread_pool_thread_max_set(apr_thread_pool_t *me,
+ apr_size_t cnt)
+{
+ unsigned int n;
+
+ me->thd_max = cnt;
+ if (0 == cnt || me->thd_cnt <= cnt) {
+ return 0;
+ }
+
+ n = (unsigned) me->thd_cnt - cnt;
+ if (n >= me->idle_cnt) {
+ trim_busy_threads(me, n - me->idle_cnt);
+ trim_idle_threads(me, 0);
+ }
+ else {
+ trim_idle_threads(me, me->idle_cnt - n);
+ }
+ return n;
+}
+
+
+apr_size_t etch_apr_thread_pool_threshold_get(apr_thread_pool_t *me)
+{
+ return me->threshold;
+}
+
+
+apr_size_t etch_apr_thread_pool_threshold_set(apr_thread_pool_t *me,
+ apr_size_t val)
+{
+ apr_size_t ov;
+
+ ov = me->threshold;
+ me->threshold = val;
+ return ov;
+}
+
+
+apr_status_t etch_apr_thread_pool_task_owner_get(apr_thread_t *thd,
+ void **owner)
+{
+ apr_status_t rv;
+ apr_thread_pool_task_t *task;
+ void *data;
+
+ rv = apr_thread_data_get(&data, "apr_thread_pool_task", thd);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ task = data;
+ if (!task) {
+ *owner = NULL;
+ return APR_BADARG;
+ }
+
+ *owner = task->owner;
+ return APR_SUCCESS;
+}
+
+
+/* vim: set ts=4 sw=4 et cin tw=80: */
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_arrayval.c b/binding-c/runtime/c/src/main/bindings/msg/etch_arrayval.c
new file mode 100644
index 0000000..b381b42
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_arrayval.c
@@ -0,0 +1,678 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_arrayval.c -- etch_arrayvalue implementation.
+ *
+ * todo: modify this class to permit using an etch_nativearray as the arrayvalue
+ * backing store. this could be very useful for large arrays of small values,
+ * where not all values are accessed individually. the key to this conversion
+ * is always returning a non-disposable object from get(), and we ensure this
+ * by always returnings item[i] from the arraylist. when a get(i) is requested,
+ * we check the arraylist first, lazy-allocating it if necessary. if arraylist[i]
+ * is null, we populate arraylist[i] from natarray[i]. we then return arraylist[i]
+ * which is always non-disposable.
+ */
+
+#include "etch_arrayval.h"
+#include "etch_tagged_data.h"
+#include "etch_nativearray.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+
+etch_arrayvalue* new_arrayvalue_init(const int, const int, const int, const int);
+etch_arrayvalue* populate_arrayvalue_from(etch_arrayvalue*);
+unsigned short etch_itemtype_to_arrayclass(unsigned short item_obj_type);
+int array_value_add(etch_arrayvalue* thisp, ETCH_ARRAY_ELEMENT* content);
+
+typedef struct etch_arrayvalue_content_wrapper {
+ etch_object object;
+ void* value;
+} etch_arrayvalue_content_wrapper;
+
+
+/**
+ * new_arrayvalue()
+ * primary constructor for etch_arrayvalue
+ * @param type_code wire ID of the array content class.
+ * @param custom_struct_type non-disposable type of custom struct,
+ caller retains ownership as with all types.
+ */
+etch_arrayvalue* new_arrayvalue (const byte type_code, etch_type* custom_struct_type,
+ const int dim, const int initsize, const int deltsize,
+ const int is_readonly, const int is_synchronized)
+{
+ etch_arrayvalue* newobj = new_arrayvalue_init(initsize, deltsize, is_readonly, is_synchronized);
+
+ newobj->dim = dim;
+ newobj->type_code = type_code;
+ ((etch_object*)newobj)->class_id = etch_arraytype_to_classid(type_code);
+
+ newobj->custom_struct_type = custom_struct_type; /* not owned */
+
+ return newobj;
+}
+
+
+/**
+ * new_arrayvalue_from()
+ * etch_arrayvalue constructor - builds arryavalue from an etch_nativearray.
+ * when native array is multi-dimensioned, this constructor is invoked recursively.
+ * todo: convert this to use the subarray call, which this code duplicates.
+ * todo: add a parameter to permit the arrayvalue to remain unpopulated; i.e.,
+ * the nativearray is present but the object array is not yet populated.
+ */
+etch_arrayvalue* new_arrayvalue_from(etch_nativearray* natarray,
+ const signed char type_code, etch_type* custom_struct_type,
+ const int initsize, const int deltsize, const int is_readonly)
+{
+ const int SUBARRAY_NUMDIMS = natarray->numdims - 1;
+ const size_t SUBARRAY_COUNT = natarray->dimension[SUBARRAY_NUMDIMS];
+ const size_t SUBARRAY_BYTELEN = natarray->dimsize [SUBARRAY_NUMDIMS];
+
+ int i = 0;
+ etch_arrayvalue* newav = NULL; /* nativearray currently max 3 dims*/
+ if (((etch_object*)natarray)->is_null) return NULL;
+ if (SUBARRAY_NUMDIMS < 0 || SUBARRAY_NUMDIMS > 2) return NULL;
+
+ newav = new_arrayvalue_init(initsize, deltsize, is_readonly, FALSE);
+ newav->is_array_owned = !is_readonly; /* does av own nativearray */
+ newav->type_code = type_code; /* external array content type */
+ ((etch_object*)newav)->class_id = etch_arraytype_to_classid (type_code);
+ newav->natarray = natarray;
+ newav->dim = natarray->numdims;
+ ((etch_object*)newav)->class_id = ((etch_object*)natarray)->class_id; /* validator expects class match */
+
+ newav->custom_struct_type = custom_struct_type; /* not owned */
+
+ if (SUBARRAY_NUMDIMS == 0) /* if single dimension, populate values */
+ {
+
+ if(natarray->content_class_id == CLASSID_UNWRAPPED){
+ arrayvalue_set_static_content(newav, FALSE);
+ }
+ else{
+ arrayvalue_set_static_content(newav, TRUE);
+ }
+ newav = populate_arrayvalue_from(newav);
+ }
+ else
+ { newav->content_obj_type = ETCHTYPEB_ARRAYVAL;
+ newav->content_item_size = sizeof(void*);
+ arrayvalue_set_static_content(newav, FALSE);
+
+ for(; i < (const int) SUBARRAY_COUNT; i++)
+ {
+ /* the native array was multi-dimensioned. we therefore add values
+ * to this arrayvalue, which are arrayalues of one dimension less than
+ * that of the parent arrayvalue. the sub-arrayvalues are created from
+ * etch_nativearrays created from offset pointers into the parent
+ * native array byte vector. sub-arrays therefore do not own content.
+ */
+ const int itemcount = (int) natarray->dimension[SUBARRAY_NUMDIMS-1];
+ const size_t vector_offset = i * SUBARRAY_BYTELEN;
+ byte* subvector = (byte*) natarray->values + vector_offset;
+ etch_arrayvalue* subav = NULL;
+
+ /* create a sub-array. note we pass a possibly unused dimension
+ * value in order to avoid the extra logic to not do so */
+ etch_nativearray* newarray = new_etch_nativearray_from(subvector, ((etch_object*)natarray)->class_id,
+ natarray->itemsize, SUBARRAY_NUMDIMS,
+ (int) natarray->dimension[0], (int) natarray->dimension[1], 0);
+
+ newarray->content_class_id = natarray->content_class_id;
+ newarray->content_obj_type = natarray->content_obj_type;
+ newarray->is_content_owned = FALSE; /* pointer into parent vector */
+ newarray->counts[0] = newarray->counts[0]; /* move counts in case */
+ newarray->counts[1] = newarray->counts[1]; /* caller is using them */
+ newarray->counts[2] = 0;
+
+ subav = new_arrayvalue_from /* recursively create a sub-arrayvalue */
+ (newarray, type_code, custom_struct_type, itemcount, 0, is_readonly);
+ subav->is_array_owned = TRUE;
+ arrayvalue_add(newav, (ETCH_ARRAY_ELEMENT*) subav);
+ }
+ }
+
+ return newav;
+}
+
+
+/**
+ * new_arrayvalue_default()
+ * default constructor for etch_arrayvalue
+ */
+etch_arrayvalue* new_arrayvalue_default ()
+{
+ etch_arrayvalue* newobj = new_arrayvalue_init
+ (ETCH_ARRAYVALUE_DEFAULT_INITSIZE,
+ ETCH_ARRAYVALUE_DEFAULT_DELTSIZE,
+ ETCH_ARRAYVALUE_DEFAULT_READONLY,
+ ETCH_ARRAYVALUE_DEFAULT_SYNCHRONIZED);
+
+ return newobj;
+}
+
+/**
+ * destroy_array_value()
+ * destructor for an etch_arrayvalue object
+ */
+int destroy_arrayvalue (void* data)
+{
+ etch_arrayvalue* thisp = (etch_arrayvalue*)data;
+
+ if (!is_etchobj_static_content(thisp))
+ {
+ if (thisp->natarray && thisp->is_array_owned)
+ etch_object_destroy(thisp->natarray);
+
+ etch_object_destroy(thisp->list);
+ }
+
+ destroy_objectex((etch_object*)thisp);
+ return 0;
+}
+
+
+void* clone_etch_arrayvalue(void* data)
+{
+ etch_arrayvalue* other = (etch_arrayvalue*)data;
+ etch_arrayvalue* newarray = NULL;
+ if(other == NULL)
+ return NULL;
+
+ newarray = new_arrayvalue(other->type_code,other->custom_struct_type,other->dim,2,2,TRUE,FALSE);
+
+ etch_object_destroy(newarray->list);
+
+
+ newarray->list = new_etch_arraylist_from(other->list);
+ newarray->list->is_readonly = 1;
+
+ return newarray;
+}
+
+
+/**
+ * new_arrayvalue_init()
+ * common initialization on etch_arrayvalue construction.
+ */
+etch_arrayvalue* new_arrayvalue_init (const int initsize, const int deltsize,
+ const int is_readonly, const int is_synchronized)
+{
+ etch_arrayvalue* newobj = (etch_arrayvalue*) new_object(sizeof(etch_arrayvalue),
+ ETCHTYPEB_ARRAYVAL, CLASSID_ARRAYVALUE);
+
+ ((etch_object*)newobj)->destroy = destroy_arrayvalue;
+ ((etch_object*)newobj)->clone = clone_etch_arrayvalue;
+
+ /* the underlying arraylist is marked as content type object, meaning we can
+ * interpret content as etchobject and call methods on the object accordingly,
+ * most notably destroy(). when is_readonly is true, the underlying list will
+ * not free memory for its content when the list is destroyed.
+ */
+ newobj->list = new_arrayvalue_arraylist(initsize, deltsize, is_readonly, is_synchronized);
+
+ return newobj;
+}
+
+
+
+/**
+ * populate_arrayvalue_from()
+ * populate the specified arrayvalue from its attached single-dimensioned native array
+ */
+etch_arrayvalue* populate_arrayvalue_from (etch_arrayvalue* av)
+{
+ etch_nativearray* nat = av->natarray;
+ const int numentries = (int) nat->dimension[0];
+ etch_object* wrapped_value = NULL;
+ int i = 0, result = 0;
+ if (nat->numdims != 1) return NULL;
+
+ /* TODO: check itemsize if this is always the size of the real element size */
+ av->content_obj_type = nat->content_obj_type;
+ av->content_item_size = (short) nat->itemsize;
+
+ for(; i < numentries; i++) {
+ result = etch_nativearray_get_wrapped_component(nat, i, &wrapped_value);
+ arrayvalue_add(av, (ETCH_ARRAY_ELEMENT*) wrapped_value);
+ }
+
+ return av;
+}
+
+
+/**
+ * arrayvalue3_to_nativearray()
+ * convert an arrayvalue of dimension 3 to a native array. assumes that an
+ * appropriately sized and configured etch_nativearray object is resident in
+ * the arrayvalue object.
+ */
+int arrayvalue3_to_nativearray(etch_arrayvalue* av2)
+{
+ const int item2count = arrayvalue_count(av2);
+ int item1count = 0, item0count = 0;
+ etch_nativearray* natv = av2->natarray;
+ etch_arrayvalue_content_wrapper* wrapper = NULL;
+ etch_arrayvalue *av1 = NULL, *av0 = NULL;
+ int i, j, k, items=0;
+
+ for(i=0; i < item2count; i++)
+ {
+ av1 = arrayvalue_get(av2, i);
+ if (!is_etch_arrayvalue(av1)) return -1;
+ item1count = arrayvalue_count(av1);
+
+ for(j=0; j < (const int) item1count; j++)
+ {
+ av0 = arrayvalue_get(av1, j);
+ if (!is_etch_arrayvalue(av0)) return -1;
+ item0count = arrayvalue_count(av0);
+
+ for(k=0; k < (const int) item0count; k++)
+ { /* insert native values into nativearray byte vector. we mask the
+ * wrapped primitive with an etch_object. we can do so because the
+ * etch primitive object is guaranteed to offset its value the
+ * same as that of an etch_object, right after the object header.
+ */
+ if (NULL == (wrapper = arrayvalue_get(av0, k))) return -1;
+ if (0 == natv->put3(natv, &wrapper->value, i, j, k))
+ items++;
+ else return -1;
+ }
+ }
+ }
+
+ return items;
+}
+
+
+/**
+ * arrayvalue2_to_nativearray()
+ * convert an arrayvalue of dimension 2 to a native array. assumes that an
+ * appropriately sized and configured etch_nativearray object is resident in
+ * the arrayvalue object.
+ */
+int arrayvalue2_to_nativearray(etch_arrayvalue* av1)
+{
+ const int item1count = arrayvalue_count(av1);
+ int item0count = 0;
+ etch_nativearray* natv = av1->natarray;
+ etch_arrayvalue_content_wrapper* wrapper = NULL;
+ etch_arrayvalue *av0 = NULL;
+ int i, j, items=0;
+
+ for(i = 0; i < item1count; i++)
+ {
+ av0 = arrayvalue_get(av1, i);
+ if (!is_etch_arrayvalue(av0)) return -1;
+ item0count = arrayvalue_count(av0);
+
+ for(j = 0; j < (const int) item0count; j++)
+ { /* insert native values into nativearray byte vector. we mask the
+ * wrapped primitive with an etch_object. we can do so because the
+ * etch primitive object is guaranteed to offset its value the
+ * same as that of an etch_object, right after the object header.
+ */
+ if (NULL == (wrapper = arrayvalue_get(av0, j))) return -1;
+ if (0 == natv->put2(natv, &wrapper->value, i, j))
+ items++;
+ else return -1;
+ }
+ }
+
+ return items;
+}
+
+
+/**
+ * arrayvalue1_to_nativearray()
+ * convert an arrayvalue of dimension 1 to a native array. assumes that an
+ * appropriately sized and configured etch_nativearray object is resident in
+ * the arrayvalue object.
+ */
+int arrayvalue1_to_nativearray(etch_arrayvalue* av)
+{
+ const int itemcount = arrayvalue_count(av);
+ etch_nativearray* natv = av->natarray;
+ etch_arrayvalue_content_wrapper* wrapper = NULL;
+ int i=0, items = 0;
+
+ for(; i < itemcount; i++)
+ { /* insert native values into nativearray byte vector. we mask the
+ * wrapped primitive with an etch_object. we can do so because the
+ * etch primitive object is guaranteed to offset its value the
+ * same as that of an etch_object, right after the object header.
+ */
+ if (NULL == (wrapper = arrayvalue_get(av, i))) return -1;
+ if (0 == natv->put1(natv, &wrapper->value, i))
+ items++;
+ else return -1;
+ }
+
+ return items;
+}
+
+
+/**
+ * arrayvalue_to_nativearray()
+ * convert an arrayvalue to a native array
+ * if the arrayvalue is created from a native array, the source native array is
+ * still resident, however this method assumes we want to create the native array
+ * regardless of whether one is resident. is also assumes that if we have created
+ * the arrayvalue from scratch, we have done so properly, i.e., a multi-dimensioned
+ * arrayvalue is and arrayvalue of arrayvalues. It additionally assumes that the
+ * arrayvalue is populated with at least one value; if this were not the case we
+ * would need to switch on the byte type_code to determine the item size.
+ */
+int arrayvalue_to_nativearray (etch_arrayvalue* av)
+{
+ int dim0count=0, dim1count=0, dim2count=0, itemsize=0;
+ etch_arrayvalue *av2 = NULL, *av1 = NULL, *av0 = NULL;
+ unsigned short array_class_id = 0;
+ etch_nativearray* newna = NULL;
+ const int dimensions = av->dim;
+
+ switch(dimensions) /* determine native array size */
+ { case 1:
+ av0 = av;
+ break;
+
+ case 2:
+ dim1count = arrayvalue_count(av);
+ av1 = (etch_arrayvalue*) arrayvalue_get(av, 0);
+ if (is_etch_arrayvalue(av1))
+ av0 = av1;
+ break;
+
+ case 3:
+ dim2count = arrayvalue_count(av);
+ av2 = (etch_arrayvalue*) arrayvalue_get(av, 0);
+ if (!is_etch_arrayvalue(av2)) break;
+
+ dim1count = arrayvalue_count(av2);
+ av1 = (etch_arrayvalue*) arrayvalue_get(av2, 0);
+ if (is_etch_arrayvalue(av1))
+ av0 = av1;
+ }
+
+ if (av0) /* if arrayvalue was populated ... */
+ { itemsize = av0->content_item_size;
+ dim0count = arrayvalue_count(av0);
+ if(av0->content_class_id == CLASSID_UNWRAPPED){
+ array_class_id = CLASSID_UNWRAPPED;
+ }else{
+ array_class_id = etch_itemtype_to_arrayclass(av0->content_obj_type);
+ }
+ }
+
+ if (dim0count == 0 || itemsize == 0)
+ return NULL; /* bad arrayvalue */
+
+ newna = new_etch_nativearray /* allocate appropriately sized native array */
+ (array_class_id, itemsize, dimensions, dim0count, dim1count, dim2count);
+ if (NULL == newna) return NULL;
+
+ /* if previous native array was attached to the arrayvalue, free it */
+ etch_object_destroy(av->natarray);
+
+ av->natarray = newna; /* attach new native array to its arrayvalue */
+
+ switch(dimensions) /* populate new native array from array value */
+ { case 1: return arrayvalue1_to_nativearray(av);
+ case 2: return arrayvalue2_to_nativearray(av);
+ case 3: return arrayvalue3_to_nativearray(av);
+ }
+
+ return NULL;
+}
+
+
+/**
+ * arrayvalue_add()
+ * returns 0 or -1
+ */
+int arrayvalue_add (etch_arrayvalue* av, ETCH_ARRAY_ELEMENT* content)
+{
+ const int result = etch_arraylist_add(av->list, content);
+ return result;
+}
+
+
+/**
+ * array_value_add()
+ * returns 0 or -1
+ */
+int array_value_add (etch_arrayvalue* av, ETCH_ARRAY_ELEMENT* content)
+{
+ const int result = etch_arraylist_add(av->list, content);
+ return result;
+}
+
+
+/**
+ * arrayvalue_get()
+ * return item at specified index
+ * returns array item, always an address, or NULL.
+ */
+void* arrayvalue_get (etch_arrayvalue* thisp, const int i)
+{
+ return etch_arraylist_get(thisp->list, i);
+}
+
+
+/**
+ * arrayvalue_count()
+ * return item count
+ */
+int arrayvalue_count (etch_arrayvalue* thisp)
+{
+ return thisp? thisp->list? thisp->list->count: 0: 0;
+}
+
+
+/**
+ * new_arrayvalue_arraylist()
+ * allocates and returns an arraylist configured appropriately for use as arrayvalue backing store
+ */
+etch_arraylist* new_arrayvalue_arraylist (const int initsize, const int deltsize,
+ const int is_readonly, const int is_synchronized)
+{
+ etch_arraylist* list = is_synchronized?
+ new_etch_arraylist_synchronized(initsize, deltsize):
+ new_etch_arraylist(initsize, deltsize);
+
+ list->is_readonly = is_readonly != 0;
+ list->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
+ return list;
+}
+
+
+static const signed char etch_primitive_typecodes[11]
+ = {ETCH_XTRNL_TYPECODE_CUSTOM, /* CLASSID_ARRAY_OBJECT */
+ ETCH_XTRNL_TYPECODE_BYTE, /* CLASSID_ARRAY_BYTE */
+ ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE, /* CLASSID_ARRAY_BOOL */
+ ETCH_XTRNL_TYPECODE_BYTE, /* CLASSID_ARRAY_BYTE */
+ ETCH_XTRNL_TYPECODE_SHORT, /* CLASSID_ARRAY_INT16 */
+ ETCH_XTRNL_TYPECODE_INT, /* CLASSID_ARRAY_INT32 */
+ ETCH_XTRNL_TYPECODE_LONG, /* CLASSID_ARRAY_INT64 */
+ ETCH_XTRNL_TYPECODE_FLOAT, /* CLASSID_ARRAY_FLOAT */
+ ETCH_XTRNL_TYPECODE_DOUBLE, /* CLASSID_ARRAY_DOUBLE */
+ ETCH_XTRNL_TYPECODE_STRING, /* CLASSID_ARRAY_STRING */
+ 0,
+ };
+
+static const unsigned short etch_arrayval_classids[11]
+ = {CLASSID_ARRAY_OBJECT, /* ETCH_XTRNL_TYPECODE_CUSTOM */
+ CLASSID_ARRAY_BYTE, /* ETCH_XTRNL_TYPECODE_BYTE */
+ CLASSID_ARRAY_BOOL, /* ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE */
+ CLASSID_ARRAY_BYTE, /* ETCH_XTRNL_TYPECODE_BYTE */
+ CLASSID_ARRAY_INT16, /* ETCH_XTRNL_TYPECODE_SHORT */
+ CLASSID_ARRAY_INT32, /* ETCH_XTRNL_TYPECODE_INT */
+ CLASSID_ARRAY_INT64, /* ETCH_XTRNL_TYPECODE_LONG */
+ CLASSID_ARRAY_FLOAT, /* ETCH_XTRNL_TYPECODE_FLOAT */
+ CLASSID_ARRAY_DOUBLE, /* ETCH_XTRNL_TYPECODE_DOUBLE */
+ CLASSID_ARRAY_STRING, /* ETCH_XTRNL_TYPECODE_STRING */
+ 0,
+ };
+
+
+/**
+ * etch_arraytype_to_classid()
+ * returns array class ID corresponding to serialization byte code.
+ */
+unsigned short etch_arraytype_to_classid (const signed char typecode)
+{
+ int i = 0;
+ unsigned short idout = 0;
+ signed char* p = (signed char*) etch_primitive_typecodes;
+
+ for(; *p; i++, p++)
+ if (*p == typecode)
+ { idout = etch_arrayval_classids[i];
+ break;
+ }
+
+ return idout;
+}
+
+
+/**
+ * etch_classid_to_arraytype()
+ * returns serialization bytecode corresponding to array class ID
+ */
+signed char etch_classid_to_arraytype (const unsigned short class_id)
+{
+ int i = 0;
+ signed char typecodeout = 0;
+ unsigned short* p = (unsigned short*) etch_arrayval_classids;
+
+ for(; *p; i++, p++)
+ if (*p == class_id)
+ { typecodeout = etch_primitive_typecodes[i];
+ break;
+ }
+
+ return typecodeout;
+}
+
+
+/**
+ * etch_itemtype_to_arrayclass()
+ * returns array object type corresponding to item object type
+ */
+unsigned short etch_itemtype_to_arrayclass(unsigned short item_obj_type)
+{
+ if (item_obj_type > 0 && item_obj_type <= ETCHTYPEB_STRING)
+ return etch_arrayval_classids[item_obj_type];
+ else return CLASSID_ARRAY_OBJECT;
+}
+
+
+/**
+ * arrayvalue_get_external_typecode()
+ * returns an array type bytecode for the specified array content type.
+ */
+signed char arrayvalue_get_external_typecode (unsigned short obj_type, unsigned short class_id)
+{
+ signed char xtype = 0;
+
+ #if(0) /* uncompiled arrays are here for documentation purposes */
+
+ unsigned short primitive_class_ids[] =
+ { CLASSID_ANY, /* 0x0 */
+ CLASSID_PRIMITIVE_BYTE, /* 0x1 */
+ CLASSID_PRIMITIVE_BOOL, /* 0x2 */
+ CLASSID_PRIMITIVE_INT8, /* 0x3 */
+ CLASSID_PRIMITIVE_INT16, /* 0x4 */
+ CLASSID_PRIMITIVE_INT32, /* 0x5 */
+ CLASSID_PRIMITIVE_INT64, /* 0x6 */
+ CLASSID_PRIMITIVE_FLOAT, /* 0x7 */
+ CLASSID_PRIMITIVE_DOUBLE,/* 0x8 */
+ CLASSID_STRING, /* 0x9 */
+ };
+
+ unsigned short primitive_obj_types[] =
+ { ETCHTYPEB_UNDEFINED, /* 0x0 */
+ ETCHTYPEB_BYTE, /* 0x1 */
+ ETCHTYPEB_BOOL, /* 0x2 */
+ ETCHTYPEB_INT8, /* 0x3 */
+ ETCHTYPEB_INT16, /* 0x4 */
+ ETCHTYPEB_INT32, /* 0x5 */
+ ETCHTYPEB_INT64, /* 0x6 */
+ ETCHTYPEB_IEEE32, /* 0x7 */
+ ETCHTYPEB_IEEE64, /* 0x8 */
+ ETCHTYPEB_STRING, /* 0x9 */
+ };
+
+ #endif
+
+ if (obj_type == ETCHTYPEB_PRIMITIVE)
+ { if (class_id < 1 || class_id > 9)
+ class_id = 0;
+
+ xtype = etch_primitive_typecodes [class_id];
+ }
+ else /* primitive obj_type? */
+ if (obj_type > 0 && obj_type <= 9)
+ xtype = etch_primitive_typecodes [obj_type];
+
+ else switch(obj_type)
+ { case ETCHTYPEB_STRUCTVAL: xtype = ETCH_XTRNL_TYPECODE_CUSTOM; break;
+ case ETCHTYPEB_ARRAYVAL: xtype = ETCH_XTRNL_TYPECODE_ARRAY; break;
+ case ETCHTYPEB_NATIVEARRAY: xtype = ETCH_XTRNL_TYPECODE_ARRAY; break;
+ default: xtype = ETCH_XTRNL_TYPECODE_CUSTOM;
+ }
+
+ return xtype;
+}
+
+
+/*
+ * arrayvalue_set_static_content()
+ * configure arraylist of object wrappers such that objects are not freed by the arraylist
+ * destructor. presumably this would be used during recursive access to arrayvalue, where
+ * lowest level objects are seen and destroyed prior to higher level objects such as the
+ * array wrappers.
+ */
+void arrayvalue_set_static_content (etch_arrayvalue* av, const int is_set)
+{
+ etch_arraylist* list = av? av->list: NULL;
+ if (list) list->is_readonly = is_set? TRUE: FALSE;
+}
+
+
+/*
+ * arrayvalue_set_iterator()
+ * set an iterator on the arrayvalue, which becomes an iterator on its list
+ */
+int arrayvalue_set_iterator (etch_arrayvalue* av, etch_iterator* iterator)
+{
+ int rv = 0;
+ if(av == NULL || iterator == NULL) {
+ return -1;
+ }
+ if(av->list == NULL) {
+ return -1;
+ }
+
+ rv = set_iterator (iterator, av->list, &av->list->iterable);
+ return rv;
+}
+
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_binary_tdi.c b/binding-c/runtime/c/src/main/bindings/msg/etch_binary_tdi.c
new file mode 100644
index 0000000..6a22c72
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_binary_tdi.c
@@ -0,0 +1,1205 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_binary_tdi.c -- binary tagged data input implementation.
+ */
+
+#include "etch_binary_tdi.h"
+#include "etch_cache.h"
+#include "etch_structval.h"
+#include "etch_arrayval.h"
+#include "etch_tagged_data.h"
+#include "etch_default_value_factory.h"
+#include "etch_encoding.h"
+#include "etch_exception.h"
+#include "etch_nativearray.h"
+#include "etch_log.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+
+static const char* LOG_CATEGORY = "etch_binary_tdi";
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * private method signatures
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+i_binary_tdi* new_binarytdi_vtable();
+
+etch_message* bintdi_start_message (tagged_data_input*);
+int bintdi_end_message (tagged_data_input*, etch_message*);
+etch_message* bintdi_read_message(tagged_data_input*, etch_flexbuffer*);
+
+etch_structvalue* bintdi_start_struct(tagged_data_input*);
+etch_structvalue* bintdi_read_struct (tagged_data_input*);
+int bintdi_end_struct (tagged_data_input*, etch_structvalue*);
+
+etch_arrayvalue* bintdi_start_array(tagged_data_input*);
+etch_arrayvalue* bintdi_read_array (tagged_data_input*, etch_validator*);
+int bintdi_end_array(tagged_data_input*, etch_arrayvalue*);
+
+etch_arrayvalue* bintdi_alloc_array(tagged_data_input*, const byte array_content_type,
+ etch_type* custom_type, const int ndims, const int nelements);
+
+int bintdi_read_keys_values(binary_tagged_data_input*, etch_structvalue*);
+int bintdi_read_values(binary_tagged_data_input*, etch_arrayvalue*, etch_validator*);
+etch_object* bintdi_read_value (binary_tagged_data_input*, etch_validator*);
+etch_object* bintdi_read_valuex(binary_tagged_data_input*, etch_validator*, boolean);
+etch_type* bintdi_get_custom_structtype(etch_object*,
+ const unsigned short, const unsigned short);
+etch_type* bintdi_read_type(binary_tagged_data_input*);
+etch_int32* bintdi_read_intvalue(binary_tagged_data_input*);
+int bintdi_read_value_rawint(binary_tagged_data_input* tdi, int* out);
+
+etch_validator* bintdi_get_validator_for(binary_tagged_data_input* tdi, etch_field* field);
+etch_object* bintdi_validate_value(binary_tagged_data_input*,
+ etch_validator*, boolean is_none_ok, etch_object*);
+etch_object* bintdi_validate_valuex(binary_tagged_data_input*,
+ etch_validator*, boolean is_none_ok, etch_object*);
+
+int bintdi_get_native_type(const signed char external_typecode, etch_array_id_params* out);
+int bintdi_get_component_type(tagged_data_input*, const byte array_content_type,
+ etch_type* custom_type, const int dims, etch_array_id_params* out);
+signed char bintagdata_get_native_typecode(const unsigned short, const unsigned short);
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * constructors/destructors
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+
+/**
+ * destroy_binary_tagged_data_input()
+ */
+int destroy_binary_tagged_data_input(void* data)
+{
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*)data;
+
+ if (!is_etchobj_static_content(tdi))
+ etch_object_destroy(tdi->impl);
+
+ /* destroy private instance data */
+ etch_free(tdi->static_nullobj);
+ etch_free(tdi->static_eodmarker);
+ clear_etchobj_static_all(tdi->static_emptystring);
+ etch_object_destroy(tdi->static_emptystring);
+
+ return destroy_objectex((etch_object*)tdi);
+}
+
+
+/**
+ * clone_tagged_data_input()
+ * tdi copy constructor. if the tdi object implements a separate instance data
+ * object, that object is cloned as well.
+ */
+void* clone_binary_tagged_data_input(void* data)
+{
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*)data;
+ binary_tagged_data_input* newtdi = (binary_tagged_data_input*) clone_object((etch_object*) tdi);
+ newtdi->impl = tdi->impl? tdi->impl->clone(tdi->impl): NULL;
+ return newtdi;
+}
+
+
+/**
+ * new_binary_tagdata_input()
+ * binary_tagged_data_input constructor
+ */
+binary_tagged_data_input* new_binary_tagdata_input(etch_value_factory* vf)
+{
+ i_binary_tdi* vtab = NULL;
+
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) new_object
+ (sizeof(binary_tagged_data_input), ETCHTYPEB_BINARYTDI, CLASSID_BINARYTDI);
+
+ ((etch_object*)tdi)->destroy = destroy_binary_tagged_data_input;
+ ((etch_object*)tdi)->clone = clone_binary_tagged_data_input;
+ tdi->vf = vf;
+
+ vtab = etch_cache_find(get_vtable_cachehkey(CLASSID_BINARYTDI_VTAB), 0);
+
+ if(!vtab)
+ { vtab = new_binarytdi_vtable();
+ etch_cache_insert(((etch_object*)vtab)->get_hashkey(vtab), vtab, FALSE);
+ }
+
+ ((etch_object*)tdi)->vtab = (vtabmask*)vtab;
+ tdi->vtor_int = etchvtor_int32_get(0);
+ tdi->static_nullobj = etchtagdata_new_nullobj(TRUE);
+ tdi->static_eodmarker = etchtagdata_new_eodmarker(TRUE);
+ tdi->static_emptystring = etchtagdata_new_emptystring(TRUE);
+ return tdi;
+}
+
+
+/**
+ * new_binary_tdi()
+ * casts result to generic tdi for use by interfaces
+ */
+tagged_data_input* new_binary_tdi(etch_value_factory* vf)
+{
+ return (tagged_data_input*) new_binary_tagdata_input(vf);
+}
+
+
+
+
+/**
+ * new_new_binarytdi_vtable()
+ * constructor for binary tdi virtual function table
+ */
+i_binary_tdi* new_binarytdi_vtable()
+{
+ etchparentinfo* inheritlist = new_etch_inheritance_list(3, 2, NULL);
+
+ i_binary_tdi* vtab
+ = new_vtable(NULL, sizeof(i_binary_tdi), CLASSID_BINARYTDI_VTAB);
+
+ /* i_tagged_data_input */
+ vtab->start_message = bintdi_start_message;
+ vtab->read_message = bintdi_read_message;
+ vtab->end_message = bintdi_end_message;
+ vtab->start_struct = bintdi_start_struct;
+ vtab->read_struct = bintdi_read_struct;
+ vtab->end_struct = bintdi_end_struct;
+ vtab->start_array = bintdi_start_array;
+ vtab->read_array = bintdi_read_array;
+ vtab->end_array = bintdi_end_array;
+
+ /* i_tagdata */
+ vtab->check_value = etchtagdata_check_value;
+ vtab->get_native_type = bintdi_get_native_type;
+ vtab->get_native_type_code = bintagdata_get_native_typecode;
+ vtab->get_custom_structtype = bintdi_get_custom_structtype;
+
+ /* inheritance */
+ inheritlist[1].o.obj_type = ETCHTYPEB_TAGDATAINP;
+ inheritlist[1].c.class_id = CLASSID_TAGDATAINP;
+ inheritlist[2].o.obj_type = ETCHTYPEB_TAGDATA;
+ inheritlist[2].c.class_id = CLASSID_TAGDATA;
+
+ vtab->inherits_from = inheritlist;
+
+ return vtab;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * read message
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_start_message()
+ * starts reading a message from the stream.
+ * returns NULL if the wire data was insufficient to construct a
+ * message object, otherwise returns the new message object,
+ * of the type read off the wire herein.
+ */
+etch_message* bintdi_start_message(tagged_data_input* tdix)
+{
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+ etch_type* typeobj = NULL; /* not owned here */
+ etch_message* newmsg = NULL; /* disposable, returned */
+ int message_itemcount = 0, result = 0;
+ byte wire_version = 0;
+
+ do
+ { result = etch_flexbuf_get_byte (tdi->flexbuf, &wire_version);
+
+ if (wire_version != ETCH_BINTAGDATA_CURRENT_VERSION)
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,"message version expected %d found %d", ETCH_BINTAGDATA_CURRENT_VERSION, wire_version);
+ break;
+ }
+
+ /* bintdi_read_type returns us a non-disposable reference to a global type,
+ * which we then give to the new message, which does not own its type.
+ */
+ if (NULL == (typeobj = bintdi_read_type (tdi))) break;
+
+ if (-1 != bintdi_read_value_rawint (tdi, &message_itemcount))
+ newmsg = new_message(typeobj, message_itemcount, tdi->vf);
+
+ } while(0);
+
+ if(newmsg) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "message receive starts\n");
+ }
+ else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "error starting message receive\n");
+ }
+ return newmsg;
+}
+
+
+/**
+ * bintdi_end_message()
+ * end message deserialization
+ */
+int bintdi_end_message(tagged_data_input* tdi, etch_message* msg)
+{
+ int result = 0;
+ /* if the newly-deserialized message is not a reply message,
+ * there is nothing to do here, since bintdi_read_keys_values()
+ * already read the eod marker. if it is a reply message, and
+ * if the message contains an exception, we will ensure that the
+ * message result object both exists and reflects that exception.
+ */
+
+ if (message_get_in_reply_to (msg))
+ {
+ etch_field* xkey = builtins._mf_msg;
+ /* look for an exception in the newly-deserialized message */
+ etch_exception* excpobj = (etch_exception*) message_get (msg, xkey);
+
+ if (is_etch_exception(excpobj))
+ { /* look for a result object in the message */
+ etch_field* resobj_key = builtins._mf_result;
+ etch_object* resobj = message_get (msg, resobj_key);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "exception found in message\n");
+ //replace result with exception object
+ //TODO memory leak here? (resobj not destroyed, should we?)
+ resobj = ((etch_object*)excpobj)->clone(excpobj);
+ result = message_put (msg, resobj_key, resobj);
+ }
+ }
+
+ return result;
+}
+
+
+/**
+ * bintdi_read_message()
+ * "non-static" read message version, accepts a tdi
+ */
+etch_message* bintdi_read_message (tagged_data_input* tdix, etch_flexbuffer* fbuf)
+{
+ etch_message* newmsg = NULL;
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+ tdi->flexbuf = fbuf;
+
+ if (NULL == (newmsg = bintdi_start_message(tdix)))
+ return NULL;
+
+ /* todo verify that this deserialization failure is eventually session_notify'ed */
+ if (-1 == bintdi_read_keys_values (tdi, newmsg->sv)) {
+ etch_object_destroy(newmsg);
+ return NULL;
+ }
+
+ bintdi_end_message(tdix, newmsg);
+ return newmsg;
+}
+
+
+/**
+ * bintdi_read_message_fromf()
+ * reads a message from the supplied flex buffer
+ */
+etch_message* bintdi_read_message_fromf (etch_value_factory* vf, etch_flexbuffer* fbuf)
+{
+ binary_tagged_data_input* tdi = new_binary_tagdata_input(vf);
+
+ return bintdi_read_message((tagged_data_input*) tdi, fbuf);
+}
+
+
+/**
+ * bintdi_read_message_from()
+ * reads a message from the supplied data buffer
+ */
+etch_message* bintdi_read_message_from (etch_value_factory* vf, byte* buf,
+ const int bufsize)
+{
+ etch_flexbuffer* fbuf = new_flexbuffer_from(buf, bufsize, bufsize, 0);
+
+ return bintdi_read_message_fromf(vf, fbuf);
+}
+
+
+/**
+ * bintdi_read_message_fromo()
+ * reads a message from the supplied data buffer, at the specified offset
+ */
+etch_message* bintdi_read_message_fromo (etch_value_factory* vf, byte* buf,
+ const int bufsize, const int msglen, const int offset)
+{
+ etch_flexbuffer* fbuf = new_flexbuffer_from(buf, bufsize, msglen, offset);
+
+ return bintdi_read_message_fromf(vf, fbuf);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * read struct
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_start_struct()
+ * starts reading a struct from the stream.
+ */
+etch_structvalue* bintdi_start_struct(tagged_data_input* tdix)
+{
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+ etch_structvalue* newsv = NULL;
+ etch_type* thistype = NULL;
+ int struct_itemcount = 0;
+
+ /* bintdi_read_type returns us a non-disposable reference to a global type,
+ * which we then give to the new structvalue, which does not own its type.
+ */
+ if (NULL == (thistype = bintdi_read_type(tdi))) {
+ return NULL;
+ }
+
+ if (-1 != bintdi_read_value_rawint(tdi, &struct_itemcount))
+ newsv = new_structvalue(thistype, struct_itemcount);
+
+ return newsv;
+}
+
+
+/**
+ * bintdi_end_struct()
+ * does nothing since read_keys_values read to end of stream
+ */
+int bintdi_end_struct(tagged_data_input* tdi, etch_structvalue* sv)
+{
+ return 0;
+}
+
+
+/**
+ * bintdi_read_struct()
+ * read a struct out of the buffer, create and return a new struct value
+ */
+etch_structvalue* bintdi_read_struct(tagged_data_input* tdix)
+{
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+ etch_structvalue* newsv = NULL;
+
+ newsv = ((struct i_binary_tdi*)((etch_object*)tdi)->vtab)->start_struct((tagged_data_input*) tdi);
+ if(newsv){
+ if (-1 == bintdi_read_keys_values(tdi, newsv)) {
+ etch_object_destroy(newsv);
+ newsv = NULL;
+ }else
+ {
+ bintdi_end_struct(tdix, newsv);
+ }
+ }
+ return newsv;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * read array
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_start_array()
+ */
+etch_arrayvalue* bintdi_start_array(tagged_data_input* tdix)
+{
+ etch_type* custom_type = NULL; /* not owned here */
+ etch_arrayvalue* arrayval = NULL; /* owned by caller */
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+ int numdimensions = 0, numelements = 0, result = 0;
+ byte array_content_type = 0;
+
+ result = etch_flexbuf_get_byte(tdi->flexbuf, &array_content_type);
+
+ switch((signed char)array_content_type)
+ { case ETCH_XTRNL_TYPECODE_CUSTOM:
+ case ETCH_XTRNL_TYPECODE_STRUCT:
+ /* bintdi_read_type returns a non-disposable type reference */
+ custom_type = bintdi_read_type(tdi);
+ if (NULL == custom_type) return NULL;
+ }
+
+ bintdi_read_value_rawint(tdi, &numdimensions);
+ bintdi_read_value_rawint(tdi, &numelements);
+
+ if(numdimensions <= 0 || numelements < 0){
+ return NULL;
+ }
+
+ /* create the arrayvalue. we pass no memory ownership here */
+ arrayval = bintdi_alloc_array(tdix, array_content_type, custom_type, numdimensions, numelements);
+ return arrayval;
+}
+
+
+/**
+ * bintdi_end_array()
+ * ends array in progress
+ */
+int bintdi_end_array(tagged_data_input* tdi, etch_arrayvalue* x)
+{
+ return 0;
+}
+
+
+/**
+ * bintdi_read_array()
+ * read an array out of the buffer, create and return a new array object
+ *
+ * todo (eventually). arrays are not optimal. following the java binding model,
+ * an array on the wire is read into an array_value. in c, this means an array
+ * of objects, one object per array element. this can make for very large data
+ * structures, 60 bytes of overhead per array element. we would like to instead
+ * use the etch_nativearray format, where an array is a blob of bytes, with
+ * attributes and methods describing how to index into the array. however
+ * it was not known if doing so would paint the c binding into a corner at
+ * some point, so for now, we create the arrayvalue just as the java binding,
+ * and export that structure to an etch_nativearray when and if we need it.
+ */
+etch_arrayvalue* bintdi_read_array(tagged_data_input* tdix, etch_validator* vtor)
+{
+ binary_tagged_data_input* tdi = (binary_tagged_data_input*) tdix;
+
+ etch_arrayvalue* newarray = bintdi_start_array((tagged_data_input*) tdi);
+ if (NULL == newarray) return NULL;
+
+ if (-1 == bintdi_read_values(tdi, newarray, vtor)) {
+ etch_object_destroy(newarray);
+ return NULL;
+ }
+
+ bintdi_end_array((tagged_data_input*)tdi, newarray);
+
+ return newarray;
+}
+
+
+/**
+ * bintdi_alloc_array()
+ * allocate an arrayvalue in which to read data for the pending array.
+ * java binding allocates a native array here. we can't do that yet since we
+ * don't have the attributes of each dimension until we get the entire array
+ * out of the stream buffer, and we want to avoid lookahead if possible.
+ * @return new array object, or NULL indicating exception condition
+ */
+etch_arrayvalue* bintdi_alloc_array(tagged_data_input* tdi, const byte array_content_type,
+ etch_type* custom_type, const int ndims, const int nelements)
+{
+ etch_array_id_params arraytype;
+ etch_arrayvalue* arrayval = NULL;
+
+ if (-1 == bintdi_get_component_type(tdi, array_content_type,
+ custom_type, ndims, &arraytype))
+ return NULL;
+
+ /* remarks regarding ownership of content for these arrayvalue objects.
+ * first, recall that their content is etch objects wrapping data read from
+ * the data buffer, and that the data so wrapped is owned by the wrapper.
+ * ownership of memory for those base data objects is assigned up the line
+ * until some object assumes responsibility. content for these arrayvalues
+ * is either those wrapper objects, at dimension[0], or arrayvalue objects
+ * at the higher dimensions. in all cases, the arryavalue owns its content,
+ * and therefore its content will be destroyed with the arrayvalue. since
+ * the destruction is recursive, destroying the top object destroys it all.
+ */
+ arrayval = new_arrayvalue(array_content_type, custom_type, ndims, nelements, 0, FALSE, 0);
+ if (NULL == arrayval) {
+ return NULL;
+ }
+
+ /* validator will validate array based on this class */
+ ((etch_object*)arrayval)->class_id = arraytype.array_class_id;
+
+ /* content type will be used later when we need to create a nativearray */
+ arrayval->content_obj_type = arraytype.content_obj_type;
+ arrayval->content_class_id = arraytype.content_class_id;
+ return arrayval;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * read from stream and reconstruct objects
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_read_intvalue()
+ * convenience method used when an encoded integer is expected next in
+ * the buffer, to read that value from the buffer and return it as an
+ * etch_int32 object, ownership of which belongs to the caller.
+ */
+etch_int32* bintdi_read_intvalue(binary_tagged_data_input* tdi)
+{
+ return (etch_int32*) bintdi_read_value(tdi, tdi->vtor_int);
+}
+
+/**
+ * bintdi_read_bytes()
+ * read all bytes from the buffer, returning those bytes
+ * @param extra pad bytes e.g. when we are reading a string and need a null term
+ */
+byte* bintdi_read_bytes(binary_tagged_data_input* tdi, const int extra, int* psize)
+{
+ byte* buf = NULL;
+ int bytecount = 0, newbufsize = 0;
+
+ if (-1 == bintdi_read_value_rawint(tdi, &bytecount)) return NULL;
+
+ if(bytecount >= 0 && (size_t) bytecount <= etch_flexbuf_avail(tdi->flexbuf)) {
+ newbufsize = bytecount + extra;
+ buf = etch_malloc(newbufsize, ETCHTYPEB_BYTES);
+ memset(buf, 0, newbufsize);
+ etch_flexbuf_get_fully(tdi->flexbuf, buf, bytecount);
+ if (psize) *psize = newbufsize;
+ }
+
+ return buf;
+}
+
+
+/**
+ * bintdi_read_type()
+ * read a type ID from the buffer, map to and return the associated static type
+ */
+etch_type* bintdi_read_type(binary_tagged_data_input* tdi)
+{
+ etch_type* type = NULL;
+ int type_id = 0;
+
+ if (0 == bintdi_read_value_rawint(tdi, &type_id))
+ type = ((struct i_value_factory*)((etch_object*)tdi->vf)->vtab)->get_type_by_id(tdi->vf, type_id);
+
+ /* note that the type object returned by valuefactory.get_type_by_id
+ * is not disposable, it is a reference into the global types map.
+ * and we are returning that nondisposable reference here */
+
+ return type;
+}
+
+
+/**
+ * bintdi_read_bytearray()
+ * read all bytes from the buffer, return a native array wrapping the resultant byte array
+ * todo: is this OK to return nativearray in one case, and arrayvalue in another?
+ * can we handle the nativearray format across the board? we can at least use nativearray
+ * for byte blobs. even if we use arravalue format we should at least use nativearray
+ * as the base of the arrayvalue, since if we need to be able to access elements, we can't
+ * simply reflect to an array as does java, we need the subscripting of the nativearray.
+ */
+etch_nativearray* bintdi_read_bytearray(binary_tagged_data_input* tdi)
+{
+ int size = 0;
+ etch_nativearray* newarray = NULL;
+ byte* buf = bintdi_read_bytes(tdi, 0, &size); /* buf is disposable, newarray will own it */
+ newarray = new_etch_nativearray_from(buf, CLASSID_ARRAY_BYTE, sizeof(byte), 1, size, 0, 0);
+ newarray->is_content_owned = TRUE;
+ return newarray;
+}
+
+
+/**
+ * bintdi_read_string()
+ * read all bytes from the buffer and translate to unicode C string
+ * @return the wrapped string
+ */
+etch_string* bintdi_read_string(binary_tagged_data_input* tdi)
+{
+ int result = 0, size = 0;
+ wchar_t* widestring = NULL;
+ etch_string* newstr = NULL;
+ byte* buf = NULL;
+
+ const int configured_encoding = tdi->vf? get_etch_string_encoding(tdi->vf): ETCH_ENCODING_DEFAULT;
+
+ const int nulltermsize = etch_encoding_get_sizeof_terminator(configured_encoding);
+
+ buf = bintdi_read_bytes(tdi, nulltermsize, &size); /* we own this memory */
+ if(buf) {
+ // TODO: pool
+ result = etch_encoding_transcode_to_wchar(&widestring, buf, configured_encoding, size, NULL);
+ etch_free(buf);
+ /* construct string object, relinquishing ownership of string buffer */
+ newstr = new_string_from(widestring, etch_encoding_for_wchar());
+ }
+ return newstr;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * read structured content
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_read_keys_values()
+ * deserializes a message from a buffer.
+ * read all key/value pairs from buffer, populating the specified struct.
+ * @param tdi caller retains.
+ * @param sv caller retains.
+ * @return 0 success, -1 deserialization failed, caller should throw exception.
+ */
+int bintdi_read_keys_values (binary_tagged_data_input* tdi, etch_structvalue* sv)
+{
+ etch_type* svtype = sv->struct_type;
+ etch_validator* thisvtor = NULL; /* non-disposable ref to type vtor */
+ etch_object* thisobj = NULL; /* disposable return from read_value */
+ etch_object* thisvalue = NULL; /* disposable return from read_value */
+ etch_field* key_field = NULL; /* non-disposable ref to static field */
+ etch_field* key_clone = NULL; /* disposable copy of structvalue key */
+ etch_int32* this_idobj = NULL; /* non-disposable copy of thisobj */
+ int result = 0;
+
+ while(result == 0)
+ {
+ thisobj = bintdi_read_valuex(tdi, tdi->vtor_int, TRUE); /* disposable */
+
+ //could not read next key field
+ if(!thisobj) {
+ break;
+ }
+
+ if (etchtagdata_is_eod (thisobj)) break; /* data end marker found */
+
+ key_clone = NULL;
+ thisvalue = NULL;
+ result = -1;
+
+ this_idobj = (etch_int32*) thisobj;
+ key_field = etchtype_get_field_by_id (svtype, this_idobj->value);
+
+ if (NULL == key_field)
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,"field lookup failed, trying to skip that value\n");
+ //skip the value of unknown keys...
+ thisvalue = bintdi_read_value(tdi, etchvtor_object_get(0)); /* returns a disposable ref */
+ //if this fails, we cannot know where we are, so stop
+ if(thisvalue == NULL) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,"cant skip value, error in deserialization\n");
+ result = -1;
+ break;
+ } else {//else we can continue
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,"skipped the value for the unknown key\n");
+ result = 0;
+ continue;
+ }
+ }
+
+ thisvtor = (etch_validator*) etchtype_get_validator_by_name(svtype, key_field->name); /* returns us a non-disposable ref */
+
+ thisvalue = bintdi_read_value(tdi, thisvtor); /* returns a disposable ref */
+ if (NULL == thisvalue){
+ break; /* validation or other deserialization error */
+ }
+
+ key_clone = (etch_field*)etch_object_clone_func(key_field);
+
+ /* structvalue_put() expects disposable key and value objects, i.e. it will
+ * call destructors on the objects when the struct is destroyed. however if
+ * the put() fails, we still own the objects, which are accounted for below.
+ * note also that this contract differs from the etch_message interface to
+ * a struct, which eats its put parameters regardless of outcome.
+ */
+ result = structvalue_put(sv, key_clone, (etch_object*) thisvalue);
+
+ //printf("read_keys_values: got field %s, value: %p\n",key_clone->aname,thisvalue);
+
+ /* if put was OK we have relinquished ownership of valuobj */
+ /* key_field was relinquished regardless. */
+
+ etch_object_destroy(thisobj);
+ thisobj = NULL;
+ }
+
+ if (-1 == result)
+ { /* some error, usually failed validation */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,"message deserialization failed\n");
+ etch_object_destroy(key_clone);
+ key_clone = NULL;
+
+ etch_object_destroy(thisvalue);
+ thisvalue = NULL;
+ }
+
+ etch_object_destroy(thisobj);
+ thisobj = NULL;
+
+ return result;
+}
+
+
+/**
+ * bintdi_read_values()
+ * read values from buffer, populating the specified array with the values so read
+ */
+int bintdi_read_values (binary_tagged_data_input* tdi,
+ etch_arrayvalue* av, etch_validator* vtor)
+{
+ int counter = 0;
+ etch_validator* ev = vtor? vtor->element_validator(vtor): NULL;
+ etch_object* thisobj = 0;
+ int result = 0;
+ if (!av || !vtor) return -1;
+
+ while(result == 0)
+ {
+
+ thisobj = bintdi_read_valuex(tdi, ev, TRUE);
+
+ if (NULL == thisobj) return -1;
+ if (etchtagdata_is_eod(thisobj)) break;
+
+ /* relinquish ownership of thisobj to the arrayvalue */
+ result = arrayvalue_add(av, thisobj);
+ if (0 == result) thisobj = NULL;
+ counter++;
+ }
+
+ etch_object_destroy(ev);
+ etch_object_destroy(thisobj);
+ return result;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * read tokens
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_read_value()
+ */
+etch_object* bintdi_read_value(binary_tagged_data_input* tdi, etch_validator* vtor)
+{
+ return bintdi_read_valuex(tdi, vtor, FALSE);
+}
+
+
+/**
+ * bintdi_read_valuex()
+ * reads a tag byte from the buffer, and based on the tag, reads zero or more
+ * bytes from the buffer, appropriate to the data type indicated by the tag.
+ * @return a *disposable* object which appropriately wraps the bytes or object
+ * read from input buffer. however if the data tag indicated a null object, a
+ * nullobj is returned, or if the tag indicated end of data, an eod object is
+ * returned. if validation fails on the object, NULL is returned, otherwise an
+ * object is always returned on which the caller is expected to call destroy().
+ */
+etch_object* bintdi_read_valuex (binary_tagged_data_input* tdi, etch_validator* v, boolean is_none_ok)
+{
+ /* if this method returns NULL it must first destroy any object created
+ * herein. normally this is accomplished in bintdi_validate_value.
+ */
+ union_alltypes u;
+ signed char objtype = 0;
+
+ if (-1 == etch_flexbuf_get_byte (tdi->flexbuf, (byte*)&objtype))
+ return NULL;
+
+ switch((signed char)objtype)
+ {
+ case ETCH_XTRNL_TYPECODE_NULL:
+ /* returns the instance null object. it is considered disposable because
+ * caller can and will call destroy() on it. however the destructor will
+ * have no effect, the null object is destroyed in the tdi destructor.*/
+ return (etch_object*) bintdi_validate_valuex (tdi, v, FALSE,
+ (etch_object*) tdi->static_nullobj);
+
+ case ETCH_XTRNL_TYPECODE_NONE:
+ /* returns the instance eod object. it is considered disposable because
+ * caller can and will call destroy() on it. however the destructor will
+ * have no effect, the eod object is destroyed in the tdi destructor. */
+ return bintdi_validate_value(tdi, v, is_none_ok,
+ (etch_object*) tdi->static_eodmarker);
+
+ case ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE:
+ return bintdi_validate_value (tdi, v, FALSE, (void*) new_boolean(FALSE));
+
+ case ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE:
+ return bintdi_validate_value (tdi, v, FALSE, (void*) new_boolean(TRUE));
+
+ case ETCH_XTRNL_TYPECODE_BYTE:
+ if (-1 == etch_flexbuf_get_byte(tdi->flexbuf, (byte*)(&u.vbyte))) break;
+ return bintdi_validate_valuex (tdi, v, FALSE, (void*) new_byte(u.vbyte));
+
+ case ETCH_XTRNL_TYPECODE_SHORT:
+ if (-1 == etch_flexbuf_get_short(tdi->flexbuf, &u.vint16)) break;
+ return bintdi_validate_valuex (tdi, v, FALSE, (void*) new_int16(u.vint16));
+
+ case ETCH_XTRNL_TYPECODE_INT:
+ if (-1 == etch_flexbuf_get_int(tdi->flexbuf, &u.vint32)) break;
+ return bintdi_validate_valuex (tdi, v, FALSE, (void*) new_int32(u.vint32));
+
+ case ETCH_XTRNL_TYPECODE_LONG:
+ if (-1 == etch_flexbuf_get_long(tdi->flexbuf, &u.vint64)) break;
+ return bintdi_validate_value (tdi, v, FALSE, (void*) new_int64(u.vint64));
+
+ case ETCH_XTRNL_TYPECODE_FLOAT:
+ if (-1 == etch_flexbuf_get_float(tdi->flexbuf, &u.vfloat)) break;
+ return bintdi_validate_value (tdi, v, FALSE, (void*) new_float(u.vfloat));
+
+ case ETCH_XTRNL_TYPECODE_DOUBLE:
+ if (-1 == etch_flexbuf_get_double(tdi->flexbuf, &u.vdouble)) break;
+ return bintdi_validate_value(tdi, v, FALSE, (void*) new_double(u.vdouble));
+
+ case ETCH_XTRNL_TYPECODE_BYTES:
+ /* must return arrayvalue for symmetry with tdo */
+ /* todo modify arrayvalue to not populate objects when so requested */
+ u.vnatarray = bintdi_read_bytearray(tdi);
+ return bintdi_validate_value (tdi, v, FALSE, (void*) u.vnatarray);
+ /*
+ u.varrayval = new_arrayvalue_from(u.vnatarray, ETCH_XTRNL_TYPECODE_BYTES,
+ NULL, (int) u.vnatarray->bytecount, 0, FALSE);
+ return bintdi_validate_value (tdi, v, FALSE, (void*) u.varrayval);
+ */
+
+ //return (etch_object*)u.vnatarray;
+ case ETCH_XTRNL_TYPECODE_EMPTY_STRING:
+ return bintdi_validate_value (tdi, v, FALSE, (void*) tdi->static_emptystring);
+
+ case ETCH_XTRNL_TYPECODE_STRING:
+ u.vstring = bintdi_read_string(tdi);
+ return bintdi_validate_value (tdi, v, FALSE, (void*) u.vstring);
+
+ case ETCH_XTRNL_TYPECODE_STRUCT:
+ u.vsv = bintdi_read_struct((void*) tdi);
+ return bintdi_validate_value (tdi, v, FALSE, (void*) u.vsv);
+
+ case ETCH_XTRNL_TYPECODE_ARRAY:
+ u.varrayval = bintdi_read_array((tagged_data_input*) tdi, v);
+ return bintdi_validate_value (tdi, v, FALSE, (void*) u.varrayval);
+
+ case ETCH_XTRNL_TYPECODE_CUSTOM:
+ {
+ etch_object* reconstituted_object = NULL;
+ /* acquire struct */
+ etch_structvalue* keys_values = bintdi_read_struct((tagged_data_input*) tdi);
+
+ //deserialization failed
+ if(! keys_values) {
+ return NULL;
+ }
+ /* relinquish struct */
+ reconstituted_object = ((struct i_value_factory*)((etch_object*)tdi->vf)->vtab)->import_custom_value(tdi->vf, keys_values);
+
+ return bintdi_validate_value (tdi, v, FALSE, (void*) reconstituted_object);
+ }
+
+ default:
+ if (is_inrange_tiny_for_signed_chars(objtype))
+ return bintdi_validate_valuex (tdi, v, FALSE, (void*) new_byte(objtype));
+ }
+
+ return NULL;
+}
+
+
+/**
+ * bintdi_read_value_rawint()
+ * read an integer value from the buffer, returning the 32-bit primitive
+ * in the out parameter.
+ * @param out a pointer to an int to receive the value read from the buffer.
+ * @return 0 success, -1 if an integer could not be read from the buffer
+ */
+int bintdi_read_value_rawint(binary_tagged_data_input* tdi, int* out)
+{
+ int thisint = 0, result = 0;
+ signed char objtype = 0;
+ union_alltypes u;
+
+ if (0 != etch_flexbuf_get_byte(tdi->flexbuf, (byte*)&objtype))
+ result = -1;
+ else
+ if (is_inrange_tiny_for_signed_chars(objtype))
+ thisint = objtype;
+ else switch(objtype)
+ {
+ case ETCH_XTRNL_TYPECODE_INT:
+ if (0 == (result = etch_flexbuf_get_int(tdi->flexbuf, &u.vint32)))
+ thisint = u.vint32;
+ break;
+ case ETCH_XTRNL_TYPECODE_SHORT:
+ if (0 == (result = etch_flexbuf_get_short(tdi->flexbuf, &u.vint16)))
+ thisint = u.vint16;
+ break;
+ case ETCH_XTRNL_TYPECODE_BYTE:
+ if (0 == (result = etch_flexbuf_get_byte(tdi->flexbuf, (byte*)&u.vbyte)))
+ thisint = u.vbyte;
+ break;
+ default:
+ result = -1;
+ }
+
+ *out = thisint;
+ return result;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * utility methods
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdi_get_component_type()
+ *
+ * @return etch c obj_type and class_id of an array of specified external type,
+ * and its content, or -1 indicating exception condition.
+ */
+int bintdi_get_component_type(tagged_data_input* tdi, const byte array_content_type,
+ etch_type* custom_type, const int dims, etch_array_id_params* out)
+{
+ if (NULL == out) return -1;
+ memset(out, 0, sizeof(etch_array_id_params));
+
+ return bintdi_get_native_type(array_content_type, out);
+}
+
+
+/*
+ * bintdi_get_native_type()
+ * returns the internal object types and class ids corresponding to the
+ * external byte typecode indicating content type of an array on the wire.
+ * class_id may or may not be significant, depending on particular obj_type.
+ */
+int bintdi_get_native_type(const signed char external_typecode, etch_array_id_params* out)
+{
+ int result = 0;
+ memset(out, 0, sizeof(etch_array_id_params));
+ out->content_obj_type = ETCHTYPEB_PRIMITIVE;
+ out->array_obj_type = ETCHTYPEB_NATIVEARRAY;
+
+
+ switch(external_typecode)
+ {
+ case ETCH_XTRNL_TYPECODE_INT:
+ out->content_class_id = CLASSID_PRIMITIVE_INT32;
+ out->array_class_id = CLASSID_ARRAY_INT32;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_STRING:
+ case ETCH_XTRNL_TYPECODE_EMPTY_STRING:
+ out->content_class_id = CLASSID_STRING;
+ out->array_class_id = CLASSID_ARRAY_STRING;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_BYTE:
+ out->content_class_id = CLASSID_PRIMITIVE_BYTE;
+ out->array_class_id = CLASSID_ARRAY_BYTE;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_LONG:
+ out->content_class_id = CLASSID_PRIMITIVE_INT64;
+ out->array_class_id = CLASSID_ARRAY_INT64;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_SHORT:
+ out->content_class_id = CLASSID_PRIMITIVE_INT16;
+ out->array_class_id = CLASSID_ARRAY_INT16;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_DOUBLE:
+ out->content_class_id = CLASSID_PRIMITIVE_DOUBLE;
+ out->array_class_id = CLASSID_ARRAY_DOUBLE;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE:
+ case ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE:
+ out->content_class_id = CLASSID_PRIMITIVE_BOOL;
+ out->array_class_id = CLASSID_ARRAY_BOOL;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_FLOAT:
+ out->content_class_id = CLASSID_PRIMITIVE_FLOAT;
+ out->array_class_id = CLASSID_ARRAY_FLOAT;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_ANY:
+ case ETCH_XTRNL_TYPECODE_ARRAY:
+ case ETCH_XTRNL_TYPECODE_STRUCT:
+ case ETCH_XTRNL_TYPECODE_CUSTOM:
+ case ETCH_XTRNL_TYPECODE_NULL:
+ out->content_obj_type = ETCHTYPEB_ETCHOBJECT;
+ out->content_class_id = CLASSID_OBJECT;
+ out->array_class_id = CLASSID_ARRAY_OBJECT;
+ break;
+
+ case ETCH_XTRNL_TYPECODE_BYTES:
+ out->content_obj_type = ETCHTYPEB_ARRAYVAL;
+ out->content_class_id = CLASSID_ARRAY_BYTE;
+ out->array_class_id = CLASSID_ARRAY_BYTE;
+ break;
+
+ default:
+ if (is_inrange_tiny_for_signed_chars(external_typecode))
+ {
+ out->content_class_id = CLASSID_PRIMITIVE_INT8;
+ out->array_class_id = CLASSID_ARRAY_INT8;
+ }
+ else
+ { out->content_obj_type = ETCHTYPEB_NONE;
+ out->content_class_id = CLASSID_NONE;
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+
+/*
+ * bintagdata_get_native_typecode()
+ * returns the external type code corresponding to internal type.
+ * etchtagdata_get_native_typecode() override
+ */
+signed char bintagdata_get_native_typecode
+ (const unsigned short obj_type, const unsigned short class_id)
+{
+ byte xtype = 0;
+
+ static const byte primitives[10]
+ = {ETCH_XTRNL_TYPECODE_CUSTOM, /* CLASSID_NONE = 0x0 */
+ ETCH_XTRNL_TYPECODE_BYTE, /* CLASSID_PRIMITIVE_BYTE = 0x1 */
+ ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE, /* CLASSID_PRIMITIVE_BOOL = 0x2 */
+ ETCH_XTRNL_TYPECODE_BYTE, /* CLASSID_PRIMITIVE_INT8 = 0x3 */
+ ETCH_XTRNL_TYPECODE_SHORT, /* CLASSID_PRIMITIVE_INT16 = 0x4 */
+ ETCH_XTRNL_TYPECODE_INT, /* CLASSID_PRIMITIVE_INT32 = 0x5 */
+ ETCH_XTRNL_TYPECODE_LONG, /* CLASSID_PRIMITIVE_INT64 = 0x6 */
+ ETCH_XTRNL_TYPECODE_FLOAT, /* CLASSID_PRIMITIVE_FLOAT = 0x7 */
+ ETCH_XTRNL_TYPECODE_DOUBLE, /* CLASSID_PRIMITIVE_DOUBLE = 0x8 */
+ ETCH_XTRNL_TYPECODE_STRING /* CLASSID_STRING = 0x9 */
+ };
+
+ switch(obj_type)
+ {
+ case ETCHTYPEB_PRIMITIVE:
+ if (class_id <= CLASSID_STRING)
+ xtype = primitives[class_id];
+ else xtype = ETCH_XTRNL_TYPECODE_CUSTOM;
+ break;
+
+ case ETCHTYPEB_ETCHOBJECT:
+ xtype = ETCH_XTRNL_TYPECODE_ANY;
+ break;
+
+ default:
+ xtype = ETCH_XTRNL_TYPECODE_CUSTOM;
+ }
+
+ return xtype;
+}
+
+
+/*
+ * bintdi_get_custom_structtype()
+ * override of etchtagdata_get_custom_structtype.
+ * defers to value factory to return a non-disposable struct type
+ * for the specified class.
+ */
+etch_type* bintdi_get_custom_structtype(etch_object* thisx,
+ const unsigned short obj_type, const unsigned short class_id)
+{
+ etch_type *static_type = NULL;
+ binary_tagged_data_input *tdi = (binary_tagged_data_input*) thisx;
+ etch_value_factory *vf = tdi->vf;
+ if(vf) static_type = ((struct i_value_factory*)((etch_object*)vf)->vtab)->get_custom_struct_type(vf, class_id);
+ return static_type;
+}
+
+
+/**
+ * bintdi_validate_value()
+ * not an override.
+ * &return an object *of the type being validated*, or null. this may be the same
+ * object as the passed value, or may be different. for example if we are working
+ * with an array of int, and a zero value was serialized, it will have been
+ * deserialized into an etch_byte, and the int validator validate_value will
+ * create and return an etch_int32 in its stead.
+ * null return indicates a validation error. null object return indicates value
+ * read was logically null. eod object return indicates end of data. if validation
+ * fails on a object, that object's destructor is invoked here.
+ */
+etch_object* bintdi_validate_value (binary_tagged_data_input* tdi,
+ etch_validator* vtor, boolean is_none_ok, etch_object* value)
+{
+ etch_object* resultobj = NULL;
+
+ if(! value)
+ return NULL;
+
+ if (NULL == vtor) {
+ resultobj = NULL;
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "no validator for type %x class %x\n", ((etch_object*)value)->obj_type, ((etch_object*)value)->class_id);
+ etch_object_destroy(value);
+ value = NULL;
+ return NULL;
+ }
+ else if (NULL == value) {
+ return NULL;
+ }
+ else if (etchtagdata_is_eod(value) && is_none_ok) {
+ resultobj = value;
+ }
+ else if (etchtagdata_is_null(value) && is_none_ok) {
+ resultobj = value;
+ }
+ else if (NULL == (resultobj = vtor->validate_value (vtor, value)))
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "validation failed for type %x class %x\n", ((etch_object*)value)->obj_type, ((etch_object*)value)->class_id);
+ etch_object_destroy(value);
+ value = NULL;
+
+ /* todo it would be nice to get an exception back across the wire here
+ * rather than kibosh the session, but not sure what the path would be
+ * to get it there, since we don't have a message yet at this point.
+ */
+ }
+
+
+ /* resultobj may be the same object as value, or may be different.
+ * if value was not an object of the type being validated, i.e. the validator
+ * is the int validator but the value object is an etch_byte representing zero,
+ * resultobj will be an etch_int32. if validation failed resultobj is null.
+ */
+ return resultobj;
+}
+
+
+/**
+ * bintdi_validate_valuex()
+ * invokes bintdi_validate_value on a value object, and if the validated object
+ * to be returned is not the same object as the passed value object, that value
+ * object's destructor is invoked. within the tdi, this will not necessarily
+ * destroy the value object, as the tdi can pass protected static objects,
+ * such as an object representing null, for validation.
+ * @return a validated object of the same class as that of the supplied validator,
+ * which may or may not be the same object as the passed value object.
+ */
+etch_object* bintdi_validate_valuex(binary_tagged_data_input* tdi,
+ etch_validator* vtor, boolean is_none_ok, etch_object* valueobj)
+{
+ etch_object* resultobj = bintdi_validate_value(tdi, vtor, is_none_ok, valueobj);
+
+ if (resultobj && valueobj && (resultobj != valueobj))
+ etch_object_destroy(valueobj);
+
+ return resultobj;
+}
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_binary_tdo.c b/binding-c/runtime/c/src/main/bindings/msg/etch_binary_tdo.c
new file mode 100644
index 0000000..f3ca824
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_binary_tdo.c
@@ -0,0 +1,807 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_binary_tdo.c -- binary tagged data output implementation.
+ */
+
+#include "etch_runtime.h"
+#include "etch_binary_tdo.h"
+#include "etch_default_value_factory.h"
+#include "etch_cache.h"
+#include "etch_arrayval.h"
+#include "etch_encoding.h"
+#include "etch_nativearray.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+static const char* LOG_CATEGORY = "etch_binary_tdo";
+
+
+byte bintagdata_get_native_typecode(const unsigned short, const unsigned short);
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * private signatures
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+i_binary_tdo* new_binarytdo_vtable();
+int bintdo_start_message(tagged_data_output*, etch_message*);
+int bintdo_write_message(tagged_data_output*, etch_message*, etch_flexbuffer*);
+int bintdo_end_message (tagged_data_output*, etch_message*);
+
+int bintdo_start_struct (tagged_data_output*, etch_structvalue*);
+int bintdo_write_struct (tagged_data_output*, etch_structvalue*);
+int bintdo_end_struct (tagged_data_output*, etch_structvalue*);
+
+int bintdo_start_array (tagged_data_output*, etch_arrayvalue*);
+int bintdo_write_array (tagged_data_output*, etch_arrayvalue*, etch_validator*);
+int bintdo_end_array (tagged_data_output*, etch_arrayvalue*);
+
+int bintdo_write_type (binary_tagged_data_output*, etch_type*);
+int bintdo_write_values(binary_tagged_data_output*, etch_arrayvalue*, etch_validator*);
+int bintdo_write_keys_values(binary_tagged_data_output*, etch_structvalue*);
+int bintdo_write_bytes_from (binary_tagged_data_output*, etch_nativearray*);
+int bintdo_write_value_rawint(binary_tagged_data_output*, const int);
+int bintdo_write_intvalue (binary_tagged_data_output*, const int);
+int bintdo_write_nonevalue(binary_tagged_data_output*);
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * constructors/destructors
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+
+/**
+ * destroy_binary_tagged_data_output()
+ */
+int destroy_binary_tagged_data_output(void* data)
+{
+ binary_tagged_data_output* tdo = (binary_tagged_data_output*)data;
+ if (!is_etchobj_static_content(tdo))
+ {
+ etch_object_destroy(tdo->impl);
+
+ if (tdo->flexbuf && tdo->is_flexbuf_owned)
+ etch_object_destroy(tdo->flexbuf);
+ }
+
+ /* destroy private instance data */
+ etch_free(tdo->static_nullobj);
+ etch_free(tdo->static_eodmarker);
+ clear_etchobj_static_all(tdo->static_emptystring);
+ etch_object_destroy(tdo->static_emptystring);
+
+ return destroy_objectex((etch_object*)tdo);
+}
+
+/**
+ * clone_tagged_data_output()
+ * tdo copy constructor. if the tdo object implements a separate instance data
+ * object, that object is cloned as well.
+ */
+void* clone_binary_tagged_data_output(void* data)
+{
+ binary_tagged_data_output* tdo = (binary_tagged_data_output*)data;
+ binary_tagged_data_output* newtdo = (binary_tagged_data_output*) clone_object((etch_object*) tdo);
+ newtdo->impl = tdo->impl? tdo->impl->clone(tdo->impl): NULL;
+ return newtdo;
+}
+
+/**
+ * new_binary_tdo()
+ * binary_tagged_data_output constructor
+ * @param vf a value factory. can be null. caller retains ownership.
+ * @param fbuf the buffer to write to. can be null. caller retains ownership.
+ */
+binary_tagged_data_output* new_binary_tagdata_output(etch_value_factory* vf, etch_flexbuffer* fbuf)
+{
+ i_binary_tdo* vtab = NULL;
+
+ binary_tagged_data_output* tdo = (binary_tagged_data_output*) new_object
+ (sizeof(binary_tagged_data_output), ETCHTYPEB_TAGDATAOUT, CLASSID_TAGDATAOUT);
+
+ ((etch_object*)tdo)->destroy = destroy_binary_tagged_data_output;
+ ((etch_object*)tdo)->clone = clone_binary_tagged_data_output;
+ tdo->flexbuf = fbuf; /* if caller passes buffer, caller owns it */
+ if (fbuf) tdo->is_flexbuf_owned = FALSE;
+ tdo->vf = vf;
+
+ vtab = etch_cache_find(get_vtable_cachehkey(CLASSID_BINARYTDO_VTAB), 0);
+
+ if(!vtab)
+ { vtab = new_binarytdo_vtable();
+ etch_cache_insert(((etch_object*)vtab)->get_hashkey(vtab), vtab, FALSE);
+ }
+
+ ((etch_object*)tdo)->vtab = (vtabmask*)vtab;
+ tdo->vtor_eod = etchvtor_eod_get();
+ tdo->vtor_int = etchvtor_int32_get(0);
+ tdo->static_nullobj = etchtagdata_new_nullobj(TRUE);
+ tdo->static_eodmarker = etchtagdata_new_eodmarker(TRUE);
+ tdo->static_emptystring = etchtagdata_new_emptystring(TRUE);
+ return tdo;
+}
+
+
+/**
+ * new_binary_tdo()
+ * casts result to generic tdo for use by interfaces
+ */
+tagged_data_output* new_binary_tdo(etch_value_factory* vf)
+{
+ return (tagged_data_output*) new_binary_tagdata_output(vf, NULL);
+}
+
+
+
+
+
+
+/**
+ * new_new_binarytdo_vtable()
+ * constructor for binary tdo virtual function table
+ */
+i_binary_tdo* new_binarytdo_vtable()
+{
+ etchparentinfo* inheritlist = new_etch_inheritance_list(3, 2, NULL);
+
+ i_binary_tdo* vtab
+ = new_vtable(NULL, sizeof(i_binary_tdo), CLASSID_BINARYTDO_VTAB);
+
+ /* i_tagged_data_input */
+ vtab->start_message = bintdo_start_message;
+ vtab->write_message = bintdo_write_message;
+ vtab->end_message = bintdo_end_message;
+ vtab->start_struct = bintdo_start_struct;
+ vtab->write_struct = bintdo_write_struct;
+ vtab->end_struct = bintdo_end_struct;
+ vtab->start_array = bintdo_start_array;
+ vtab->write_array = bintdo_write_array;
+ vtab->end_array = bintdo_end_array;
+
+ /* i_tagdata */
+ #if(0)
+ vtab->check_value = etchtagdata_check_value;
+ vtab->get_native_type = bintdo_get_native_type;
+ vtab->get_native_type_code = bintdo_get_native_typecode;
+ vtab->get_custom_structtype = bintdo_get_custom_structtype;
+ #endif
+
+ /* inheritance */
+ inheritlist[1].o.obj_type = ETCHTYPEB_TAGDATAOUT;
+ inheritlist[1].c.class_id = CLASSID_TAGDATAOUT;
+ inheritlist[2].o.obj_type = ETCHTYPEB_TAGDATA;
+ inheritlist[2].c.class_id = CLASSID_TAGDATA;
+
+ vtab->inherits_from = inheritlist;
+
+ return vtab;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * write message
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdo_start_message()
+ * message is unique among serialized objects in that since message is at the
+ * top level, no type byte is written to mark the start of a message. a version
+ * number is written to identify the btd implementation version used to write
+ * the message.
+ */
+int bintdo_start_message(tagged_data_output* tdox, etch_message* msg)
+{
+ binary_tagged_data_output* tdo = (binary_tagged_data_output*) tdox;
+ ETCH_ASSERT(tdo && msg);
+ etch_flexbuf_put_byte(tdo->flexbuf, ETCH_BINTAGDATA_CURRENT_VERSION);
+ return bintdo_start_struct(tdox, msg->sv);
+}
+
+
+ /**
+ * bintdo_write_message()
+ * message is unique among serialized objects in that since message is at the
+ * top level, no type byte is written to mark the start of a message. a version
+ * number is written to identify the btd implementation version used to write
+ * the message.
+ */
+int bintdo_write_message(tagged_data_output* tdox, etch_message* msg, etch_flexbuffer* fbuf)
+{
+ binary_tagged_data_output* tdo = (binary_tagged_data_output*) tdox;
+ ETCH_ASSERT(tdo && msg && fbuf);
+ tdo->flexbuf = fbuf;
+
+ if (-1 == bintdo_start_message(tdox, msg)) return -1;
+
+ if (-1 == bintdo_write_keys_values(tdo, msg->sv)) return -1;
+
+ return bintdo_end_message(tdox, msg);
+}
+
+
+/**
+ * tdo_end_message()
+ * marks the end of the message in process.
+ */
+int bintdo_end_message(tagged_data_output* tdox, etch_message* msg)
+{
+ return bintdo_end_struct(tdox, msg->sv);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * write struct
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdo_start_struct()
+ * write the beginning of struct data.
+ */
+int bintdo_start_struct(tagged_data_output* tdox, etch_structvalue* sv)
+{
+ binary_tagged_data_output* tdo = (binary_tagged_data_output*) tdox;
+ ETCH_ASSERT(tdo && sv);
+ /* caller has already written a bytecode to the buffer indicating struct follows */
+
+ if (-1 == bintdo_write_type(tdo, sv->struct_type)) return -1;
+
+ return bintdo_write_value_rawint(tdo, structvalue_count(sv));
+}
+
+
+/**
+ * bintdo_write_struct()
+ */
+int bintdo_write_struct(tagged_data_output* tdox, etch_structvalue* sv)
+{
+ ETCH_ASSERT(tdox && sv);
+
+ if (-1 == bintdo_start_struct(tdox, sv)) return -1;
+
+ if (-1 == bintdo_write_keys_values((binary_tagged_data_output*)tdox, sv)) return -1;
+
+ return bintdo_end_struct(tdox, sv);
+}
+
+
+/**
+ * bintdo_end_struct()
+ * mark end of specified struct
+ */
+int bintdo_end_struct(tagged_data_output* tdox, etch_structvalue* sv)
+{
+ return bintdo_write_nonevalue((binary_tagged_data_output*) tdox);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * write array
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdo_start_array()
+ * starts writing of an array
+ */
+int bintdo_start_array (tagged_data_output* tdox, etch_arrayvalue* av)
+{
+ int errs = 0;
+ binary_tagged_data_output* tdo = (binary_tagged_data_output*) tdox;
+ ETCH_ASSERT(tdo && av);
+
+ etch_flexbuf_put_byte (tdo->flexbuf, av->type_code);
+
+ if (ETCH_XTRNL_TYPECODE_CUSTOM == av->type_code)
+ errs += bintdo_write_type (tdo, av->custom_struct_type);
+
+ errs += bintdo_write_value_rawint (tdo, av->dim);
+ errs += bintdo_write_value_rawint (tdo, arrayvalue_count(av));
+ return errs? -1: 0;
+}
+
+
+/**
+ * bintdo_write_array()
+ */
+int bintdo_write_array (tagged_data_output* tdox, etch_arrayvalue* av, etch_validator* vtor)
+{
+ ETCH_ASSERT(tdox && vtor);
+
+ if (!is_etch_arrayvalue(av)) return -1;
+
+ if (-1 == bintdo_start_array (tdox, av)) return -1;
+
+ if (-1 == bintdo_write_values ((binary_tagged_data_output*) tdox, av, vtor))
+ return -1;
+
+ return bintdo_end_array(tdox, av);
+}
+
+
+/**
+ * bintdo_end_array()
+ * writes end of the array being read.
+ */
+int bintdo_end_array (tagged_data_output* tdo, etch_arrayvalue* av)
+{
+ return bintdo_write_nonevalue ((binary_tagged_data_output*) tdo);
+}
+
+
+/**
+ * bintdo_to_arrayvalue()
+ * convert supplied native array to an etch_arrayvalue.
+ * @param na the native array. caller retains.
+ * @return an etch_arrayvalue. caller owns it.
+ */
+etch_arrayvalue* bintdo_to_arrayvalue (binary_tagged_data_output* tdo, etch_nativearray* na)
+{
+ etch_type* NULLTYPE = NULL;
+
+ signed char content_typecode = arrayvalue_get_external_typecode(na->content_obj_type, na->content_class_id);
+
+ /* todo we should calculate array size from native array
+ * metadata rather than creating it using a default size */
+ etch_arrayvalue* av = NULL;
+ if(((etch_object*)na)->class_id != CLASSID_ARRAY_OBJECT && content_typecode == ETCH_XTRNL_TYPECODE_CUSTOM) {
+ etch_type* theType = NULL;
+ //theType = class_to_type_map_get(tdo->vf)->class_to_type, na->content_class_id);
+ theType = ((struct i_value_factory*)((etch_object*)tdo->vf)->vtab)->get_custom_struct_type(tdo->vf, ETCHMAKECLASS(na->content_obj_type, na->content_class_id));
+ av = new_arrayvalue_from (na, content_typecode, theType, ETCH_DEFSIZE, ETCH_DEFSIZE, TRUE);
+ av->content_class_id = na->content_class_id;
+ av->custom_struct_type = theType;
+ }
+ else
+ if(((etch_object*)na)->class_id == CLASSID_ARRAY_OBJECT && content_typecode == ETCH_XTRNL_TYPECODE_CUSTOM) {
+ av = new_arrayvalue_from (na, ETCH_XTRNL_TYPECODE_ANY, NULLTYPE, ETCH_DEFSIZE, ETCH_DEFSIZE, TRUE);
+ av->content_class_id = na->content_class_id;
+ }
+ else {
+ av = new_arrayvalue_from (na, content_typecode, NULLTYPE, ETCH_DEFSIZE, ETCH_DEFSIZE, TRUE);
+ av->content_class_id = na->content_class_id;
+ }
+
+ if(NULL == av) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "nativearray conversion failed");
+ }
+
+
+
+
+ return av;
+}
+
+
+///**
+// * normalize_etch_array()
+// * validate parameter as an array type with dimension <= that specified,
+// * and convert to arrayvalue if necessary.
+// * @param a an arrayvalue or nativearray. caller retains.
+// * @param maxdim maximum number of dimensions, zero means don't validate dimensions.
+// * @return the passed array expressed as an to arrayvalue, or NULL if error.
+// */
+//etch_arrayvalue* normalize_etch_array(void* a, const int maxdim)
+//{
+// etch_arrayvalue* av = NULL;
+//
+// if (is_etch_nativearray(a))
+// { etch_nativearray* na = (etch_nativearray*) a;
+// if (0 == maxdim || na->numdims <= maxdim)
+// av = bintdo_to_arrayvalue(na);
+// }
+// else
+// if (is_etch_arrayvalue(a))
+// { etch_arrayvalue* xav = (etch_arrayvalue*) a;
+// if (0 == maxdim || xav->dim <= maxdim)
+// av = xav;
+// }
+//
+// return av;
+//}
+
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * disassemble objects and write bytes
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * bintdo_write_nonevalue()
+ * convenience method to write eod marker
+ */
+int bintdo_write_nonevalue(binary_tagged_data_output* tdo)
+{
+ return bintdo_write_value(tdo, tdo->vtor_eod, tdo->static_eodmarker);
+}
+
+
+/**
+ * bintdo_write_intvalue()
+ * convenience method used when an encoded integer is expected next in
+ * the buffer, to write such a value to the buffer.
+ *
+ * this method is no longer used, we now use bintdo_write_value_rawint()
+ */
+int bintdo_write_intvalue(binary_tagged_data_output* tdo, const int value)
+{
+ etch_int32* intobj = new_int32(value);
+ int result = bintdo_write_value(tdo, tdo->vtor_int, (etch_object*) intobj);
+ etch_object_destroy(intobj);
+ return result;
+}
+
+
+/**
+ * bintdo_write_value_rawint()
+ * write specified 32-bit integer value to the buffer
+ */
+int bintdo_write_value_rawint(binary_tagged_data_output* tdo, const int value)
+{
+ size_t nout = 0;
+
+ if (is_inrange_tiny(value))
+ nout = etch_flexbuf_put_byte(tdo->flexbuf, (signed char) value);
+ else
+ if (is_inrange_byte(value))
+ if (sizeof(byte) == etch_flexbuf_put_byte(tdo->flexbuf, ETCH_XTRNL_TYPECODE_BYTE))
+ nout = etch_flexbuf_put_byte(tdo->flexbuf, (signed char) value);
+ else;
+ else
+ if (is_inrange_int16(value))
+ if (sizeof(byte) == etch_flexbuf_put_byte(tdo->flexbuf, ETCH_XTRNL_TYPECODE_SHORT))
+ nout = etch_flexbuf_put_short(tdo->flexbuf, (short) value);
+ else;
+ else
+ if (sizeof(byte) == etch_flexbuf_put_byte(tdo->flexbuf, ETCH_XTRNL_TYPECODE_INT))
+ nout = etch_flexbuf_put_int(tdo->flexbuf, value);
+
+ return nout? 0: -1;
+}
+
+
+/**
+ * bintdo_write_type()
+ * convenience method used when an etch_type is to be written to the buffer,
+ * to write such a value to the buffer. caller owns the supplied type.
+ * only the type's id is written.
+ */
+int bintdo_write_type(binary_tagged_data_output* tdo, etch_type* type)
+{
+ return type? bintdo_write_value_rawint(tdo, type->id): -1;
+}
+
+
+/**
+ * bintdo_get_bytes()
+ * gets serialized bytes of the specified message. caller owns returned byte vector.
+ * not sure what this is used for.
+ * "static" method, no tdo is passed.
+ * @return count of bytes
+ */
+int bintdo_get_bytes(etch_message* msg, etch_value_factory* vf, byte** out)
+{
+ size_t bytecount = 0;
+ etch_flexbuffer* fbuf = new_flexbuffer(0); /* tdo will own this */
+ binary_tagged_data_output* tdo = new_binary_tagdata_output(vf, fbuf);
+ bintdo_write_message((tagged_data_output*)tdo, msg, fbuf);
+
+ *out = etch_flexbuf_get_all(fbuf, &bytecount); /* new allocation */
+
+ etch_object_destroy(tdo);
+ return (int) bytecount;
+}
+
+
+/**
+ * bintdo_write_bytes()
+ * writes a byte vector to the buffer.
+ */
+int bintdo_write_bytes(binary_tagged_data_output* tdo, char* bytes, const int bytecount)
+{
+ size_t nout = 0;
+ /* TODO handle return value */
+ bintdo_write_value_rawint(tdo, (int) bytecount);
+
+ nout = etch_flexbuf_put(tdo->flexbuf, (unsigned char*)bytes, 0, bytecount);
+
+ return nout == bytecount? 0: -1;
+}
+
+
+/**
+ * bintdo_write_bytes_from()
+ * writes a byte vector from a native array to the buffer.
+ * @param bytearray an etch_nativearray of single dimension and of content type byte.
+ */
+int bintdo_write_bytes_from(binary_tagged_data_output* tdo, etch_nativearray* bytearray)
+{
+ /* we're assuming we always get a nativearray object and not a char*,
+ * however i'm not sure yet exactly where the tdo input is created,
+ * so i'm not positive this is the way is should be. update: perhaps this
+ * method should be passed an arrayvalue, keep an eye on this.
+ */
+ size_t bytecount = 0, nout = 0;
+ int result = 0;
+
+ //if ((is_etch_nativearray(bytearray))
+ //&& (((etch_object*)bytearray)->class_id = CLASSID_ARRAY_BYTE)
+ //&& (bytearray->numdims == 1));
+ //else return -1;
+
+ bytecount = bytearray->bytecount; /* or bytearray->dimsize[0], same thing */
+
+ result = bintdo_write_value_rawint(tdo, (int) bytecount);
+
+ nout = etch_flexbuf_put(tdo->flexbuf, bytearray->values, 0, bytecount);
+
+ return nout == bytecount? 0: -1;
+}
+
+
+/**
+ * bintdo_write_string()
+ * writes a string value to the buffer.
+ */
+int bintdo_write_string(binary_tagged_data_output* tdo, etch_string* s)
+{
+ int result = 0, wire_encoding = 0, this_encoding = 0, is_new_memory = 0;
+ int bytes_to_write = 0, bytes_written = 0;
+ unsigned char* bytevector = NULL;
+ unsigned int terminator = 0;
+ if (NULL == s) return -1;
+
+ wire_encoding = get_etch_string_encoding(tdo->vf);
+ this_encoding = s->encoding;
+
+ if (wire_encoding == this_encoding) {
+ bytevector = s->v.value;
+ bytes_to_write = s->byte_count;
+ } else {
+ // TODO: pool
+ result = etch_encoding_transcode((char**)&bytevector, wire_encoding, s->v.value, this_encoding, s->byte_count, &bytes_to_write, NULL);
+ ETCH_ASSERT(result != -1);
+ is_new_memory = TRUE;
+ }
+ terminator = etch_encoding_get_sizeof_terminator(wire_encoding);
+ if ((unsigned int)bytes_to_write > terminator) {
+ bytes_to_write -= terminator;
+ }
+
+ if (NULL == bytevector) return -1;
+
+ result = bintdo_write_value_rawint(tdo, bytes_to_write);
+
+ bytes_written = (int) etch_flexbuf_put(tdo->flexbuf, bytevector, 0, bytes_to_write);
+ result = bytes_written == bytes_to_write? 0: -1;
+
+ if (is_new_memory)
+ etch_free(bytevector);
+
+ return result;
+}
+
+
+/**
+ * bintdo_write_values()
+ * write all values from the specified array
+ */
+int bintdo_write_values(binary_tagged_data_output* tdo, etch_arrayvalue* av,
+ etch_validator* vtor)
+{
+ int errs = 0;
+ etch_validator* ev = vtor? vtor->element_validator(vtor): NULL;
+ etch_iterator iterator;
+ set_iterator(&iterator, av->list, &av->list->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ errs += (0 != bintdo_write_value(tdo, ev, iterator.current_value));
+ iterator.next(&iterator);
+ }
+
+ etch_object_destroy(ev);
+
+ return errs? -1: 0;
+}
+
+
+/**
+ * bintdo_write_keys_values()
+ * write key/value pairs from the struct to the buffer
+ */
+int bintdo_write_keys_values (binary_tagged_data_output* tdo, etch_structvalue* sv)
+{
+ etch_type* struct_type = sv->struct_type;
+ etch_validator* thisvtor = NULL;
+ etch_field* thiskey = NULL;
+ etch_object* thisval = NULL;
+ int result = 0;
+
+ etch_iterator iterator;
+ set_iterator(&iterator, sv->items, &sv->items->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ thiskey = (etch_field*) iterator.current_key;
+ thisval = (etch_object*) iterator.current_value;
+ ETCH_ASSERT(thiskey);
+
+ thisvtor = (etch_validator*)
+ etchtype_get_validator_by_name (struct_type, thiskey->name);
+
+ if (NULL == thisvtor)
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "type '%s' missing validator '%s'\n", struct_type->aname, thiskey->aname);
+ result = -1;
+ break;
+ }
+
+ result = bintdo_write_value_rawint (tdo, thiskey->id);
+ result = bintdo_write_value (tdo, thisvtor, thisval);
+ if (-1 == result) break;
+
+ iterator.next(&iterator);
+ }
+
+ return result;
+}
+
+
+/**
+ * bintdo_write_value()
+ * write specified value to the buffer
+ * @param vtor validator for specified value, or null if none
+ * @param value the value to be encoded and written, as a *non-disposable* object,
+ * i.e. caller owns memory for the value object.
+ */
+int bintdo_write_value (binary_tagged_data_output* tdo, etch_validator* vtor, etch_object* value)
+{
+ etch_config_t* config = NULL;
+ int32 propvalue = 0;
+ int result = 0;
+ size_t nout = 0;
+ union_alltypes u;
+ signed char external_typecode;
+
+ etch_runtime_get_config(&config);
+ ETCH_ASSERT(config);
+
+ etch_config_get_property_int(config, "etch.validate.write", &propvalue);
+ if (propvalue == 1)
+ { /* we should disable validate on write in production */
+ if (NULL == value); /* don't recall why null value is not validated */
+ else
+ if (!vtor || -1 == vtor->validate (vtor, (etch_object*) value))
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "validation failed for type %x class %x\n", ((etch_object*)value)->obj_type, ((etch_object*)value)->class_id);
+ return -1;
+ }
+ }
+
+ /* determine tag (fyi signed only because using the java byte constants) */
+ external_typecode = etchtagdata_check_value((etch_object*) value);
+
+ /* write tag */
+ if (sizeof(byte) != etch_flexbuf_put_byte(tdo->flexbuf, external_typecode))
+ return -1;
+
+ switch(external_typecode)
+ {
+ case ETCH_XTRNL_TYPECODE_NULL:
+ case ETCH_XTRNL_TYPECODE_NONE:
+ case ETCH_XTRNL_TYPECODE_EMPTY_STRING:
+ case ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE:
+ case ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE:
+ return 0; /* nothing to do, tag says it all */
+
+ case ETCH_XTRNL_TYPECODE_BYTE:
+ if (0 == etchtagdata_byte_value((etch_object*) value, (byte*)&u.vbyte))
+ nout = etch_flexbuf_put_byte(tdo->flexbuf, u.vbyte);
+ break;
+
+ case ETCH_XTRNL_TYPECODE_INT:
+ if (0 == etchtagdata_int32_value((etch_object*) value, &u.vint32))
+ nout = etch_flexbuf_put_int(tdo->flexbuf, u.vint32);
+ break;
+
+ case ETCH_XTRNL_TYPECODE_LONG:
+ if (0 == etchtagdata_int64_value((etch_object*) value, &u.vint64))
+ nout = etch_flexbuf_put_long(tdo->flexbuf, u.vint64);
+ break;
+
+ case ETCH_XTRNL_TYPECODE_SHORT:
+ if (0 == etchtagdata_int16_value((etch_object*) value, &u.vint16))
+ nout = etch_flexbuf_put_short(tdo->flexbuf, u.vint16);
+ break;
+
+ case ETCH_XTRNL_TYPECODE_DOUBLE:
+ if (0 == etchtagdata_double_value((etch_object*) value, &u.vdouble))
+ nout = etch_flexbuf_put_double(tdo->flexbuf, u.vdouble);
+ break;
+
+ case ETCH_XTRNL_TYPECODE_FLOAT:
+ if (0 == etchtagdata_float_value((etch_object*) value, &u.vfloat))
+ nout = etch_flexbuf_put_float(tdo->flexbuf, u.vfloat);
+ break;
+
+ case ETCH_XTRNL_TYPECODE_BYTES:
+ /* we get an arrayvalue here. to do differently would be problematic
+ * without rewriting higher levels to not work with arrayvalue.
+ * perhaps we should simply pass arrayvalue to bintdo_write_bytes_from().
+ * TODO either accept a native array here, or change
+ * bintdo_write_bytes_from() to accept an arrayvalue, or both.
+ */
+ if(is_etch_nativearray(value)){
+ u.vnatarray = (etch_nativearray*)value;
+ result = bintdo_write_bytes_from(tdo,u.vnatarray);
+ return result;
+ }
+ else{
+ u.vnatarray = ((etch_arrayvalue*)value)->natarray;
+ result = bintdo_write_bytes_from(tdo, u.vnatarray);
+ return result;
+ }
+
+ case ETCH_XTRNL_TYPECODE_ARRAY:
+ /* if arriving here from client app we may get an etch_nativearray,
+ * which we must convert to an arrayvalue now.
+ * TODO write a version of bintdo_write_array which accepts a
+ * nativearray, so we can avoid this to_arrayvalue() conversion.
+ */
+ if (is_etch_nativearray(value)) {
+ u.varrayval = bintdo_to_arrayvalue(tdo, (etch_nativearray*) value);
+ result = bintdo_write_array((tagged_data_output*)tdo, u.varrayval, vtor);
+ etch_object_destroy(u.varrayval);
+ }
+ else {
+ u.varrayval = (etch_arrayvalue*) value;
+ result = bintdo_write_array((tagged_data_output*)tdo, u.varrayval, vtor);
+ }
+ return result;
+
+ case ETCH_XTRNL_TYPECODE_STRING:
+ { etch_string* s = (etch_string*) value;
+ result = bintdo_write_string(tdo, s);
+ return result;
+ }
+
+ case ETCH_XTRNL_TYPECODE_CUSTOM:
+ {
+ etch_structvalue* sv = ((struct i_value_factory*)((etch_object*)tdo->vf)->vtab)->export_custom_value(tdo->vf, value);
+ if (NULL == sv) return -1;
+
+ result = bintdo_write_struct((tagged_data_output*) tdo, sv);
+
+ etch_object_destroy(sv);
+ return result;
+ }
+
+ default:
+ return is_inrange_tiny_for_signed_chars(external_typecode)? 0: -1;
+ }
+
+ return nout? 0: -1;
+}
+
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_default_value_factory.c b/binding-c/runtime/c/src/main/bindings/msg/etch_default_value_factory.c
new file mode 100644
index 0000000..958172e
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_default_value_factory.c
@@ -0,0 +1,896 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_defvalufact.c
+ * default value factory from which all others inherit
+ */
+
+#include "etch_default_value_factory.h"
+#include "etch_serializer.h"
+#include "etch_map.h"
+#include "etch_log.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include <wchar.h>
+
+char* ETCHVALF = "VALF";
+
+void etchvf_instantiate_builtins();
+
+etch_type* defvf_add_type (void*, etch_type*);
+etch_id_name* get_idname_by_id (etch_hashtable* map, const unsigned id);
+etch_type* defvf_get_type_by_id (void*, const unsigned id);
+etch_type* defvf_get_type_by_name(void*, const wchar_t* name);
+etch_type* defvf_get_type_by_name(void*, const wchar_t* name);
+wchar_t* defvf_get_string_encoding (void*);
+etch_type* defvf_get_custom_struct_type (void*, const unsigned);
+etch_int64* defvf_get_message_id (void*, etch_message*);
+int defvf_set_message_id (void*, etch_message*, etch_int64* id);
+etch_int64* defvf_get_in_reply_to (void*, etch_message*);
+int defvf_set_in_reply_to (void*, etch_message*, etch_int64* id);
+etch_type* defvf_get_mt_exception(void*);
+etch_type* defvf_get_mt_rutime_exception(void*);
+etch_type* defvf_get_mt_auth_exception(void*);
+etch_object* defvf_import_custom_value (void*, etch_structvalue*);
+etch_structvalue* defvf_export_custom_value (void*, etch_object* value);
+etch_arraylist* new_vf_mixin_collection(void*);
+etch_arraylist* defvf_get_types (void*);
+
+
+/*
+ * built-in (etch-global, quasi-static) objects.
+ * these singleton objects are global to all vfs, instantiated with the
+ * initial vf, and destroyed at etch teardown.
+ */
+defvf_statics builtins;
+unsigned char is_builtins_instantiated;
+
+const wchar_t* str_etch_runtime_exception;
+const wchar_t* str_etch_auth_exception;
+const wchar_t* str_exception;
+const wchar_t* str_etch_list;
+const wchar_t* str_etch_map;
+const wchar_t* str_etch_set;
+const wchar_t* str_etch_datetime;
+
+const wchar_t* str_msg;
+const wchar_t* str_message_id;
+const wchar_t* str_in_reply_to;
+const wchar_t* str_result;
+
+const wchar_t* str_utf8;
+const wchar_t* str_keys;
+const wchar_t* str_values;
+const wchar_t* str_date_time;
+const wchar_t* str_keys_values;
+
+const wchar_t* str_msgizervf;
+const wchar_t* str_msgizerfmt;
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * constructors/destructors
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_default_value_factory()
+ * constructor for default_value_factory
+ * @param typemap the types map. if caller supplies this object, caller
+ * retains ownership, unless vf.is_own_types flag is subsequently set false.
+ * @param c2tmap the class to type map. if caller supplies this object, caller
+ * retains ownership, unless vf.is_own_class_to_type flag is subsequently set false.
+ */
+default_value_factory* new_default_value_factory(vf_idname_map* typemap, class_to_type_map* c2tmap)
+{
+ return new_default_value_factory_ex(sizeof(default_value_factory), typemap, c2tmap);
+}
+
+/**
+ * destroy_default_value_factory()
+ * destructor for default_value_factory
+ */
+int destroy_default_value_factory(void* data)
+{
+ default_value_factory* vf = (default_value_factory*)data;
+
+ if (!is_etchobj_static_content(vf))
+ {
+ etch_object_destroy(vf->mixins);
+ etch_object_destroy(vf->impl);
+ }
+
+ return destroy_objectex((etch_object*) vf);
+}
+
+/**
+ * new_default_value_factory_ex()
+ * constructor for default_value_factory
+ * @param objsize the size of the value factory
+ * @param typemap the types map. if caller supplies this object, caller
+ * retains ownership, unless vf.is_own_types flag is subsequently set false.
+ * @param c2tmap the class to type map. if caller supplies this object, caller
+ * retains ownership, unless vf.is_own_class_to_type flag is subsequently set false.
+ */
+default_value_factory* new_default_value_factory_ex(const int objsize, vf_idname_map* typemap, class_to_type_map* c2tmap)
+{
+ i_value_factory* vtab = NULL;
+ default_value_factory* newvf = NULL;
+
+ ETCH_ASSERT("typemap==NULL" && typemap);
+ ETCH_ASSERT("c2tmap==NULL" && c2tmap);
+
+ newvf = (default_value_factory*) new_value_factory(objsize);
+ ((etch_object*)newvf)->destroy = destroy_default_value_factory;
+
+ vtab = (i_value_factory*)((etch_object*)newvf)->vtab;
+ vtab->add_type = defvf_add_type;
+ vtab->export_custom_value = defvf_export_custom_value;
+ vtab->get_custom_struct_type= defvf_get_custom_struct_type;
+ vtab->get_in_reply_to = defvf_get_in_reply_to;
+ vtab->get_message_id = defvf_get_message_id;
+ vtab->get_string_encoding = defvf_get_string_encoding;
+ vtab->get_type_by_id = defvf_get_type_by_id;
+ vtab->get_type_by_name = defvf_get_type_by_name;
+ vtab->get_types = defvf_get_types;
+ vtab->import_custom_value = defvf_import_custom_value;
+ vtab->set_in_reply_to = defvf_set_in_reply_to;
+ vtab->set_message_id = defvf_set_message_id;
+
+ newvf->mixins = new_vf_mixin_collection(newvf);
+ newvf->types = typemap;
+ newvf->class_to_type = c2tmap;
+ return newvf;
+}
+
+
+
+int
+defvf_initialize_static(vf_idname_map* typemap, class_to_type_map* c2tmap){
+ int result = 0;
+
+ struct i_hashtable* vtab =((struct i_hashtable*)((etch_object*)typemap)->vtab);
+
+ etchvf_instantiate_builtins();
+
+ vtab->inserth
+ (typemap->realtable, builtins._mt__etch_runtime_exception, NULL,typemap, 0);
+ vtab->inserth
+ (typemap->realtable, builtins._mt__etch_auth_exception, NULL,typemap, 0);
+ vtab->inserth
+ (typemap->realtable, builtins._mt__exception, NULL,typemap, 0);
+ vtab->inserth
+ (typemap->realtable, builtins._mt__etch_list, NULL,typemap, 0);
+ vtab->inserth
+ (typemap->realtable, builtins._mt__etch_map, NULL,typemap, 0);
+ vtab->inserth
+ (typemap->realtable, builtins._mt__etch_set, NULL,typemap, 0);
+ vtab->inserth
+ (typemap->realtable, builtins._mt__etch_datetime, NULL,typemap, 0);
+
+ etch_serializer_exception_init(builtins._mt__exception, c2tmap);
+ etch_serializer_authxcp_init (builtins._mt__etch_auth_exception, c2tmap);
+ etch_serializer_rtxcp_init (builtins._mt__etch_runtime_exception, c2tmap);
+ etch_serializer_list_init (builtins._mt__etch_list,c2tmap);
+ etch_serializer_map_init (builtins._mt__etch_map, c2tmap);
+ etch_serializer_set_init (builtins._mt__etch_set, c2tmap);
+ etch_serializer_date_init (builtins._mt__etch_datetime, c2tmap);
+ return result;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * vf class methods
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * defvf_add_type()
+ * adds a type to set of types
+ * @param type a non-disposable etch_type object
+ * @return the effective type. if there was a name collision, the existing type
+ * is returned in place of the supplied type, AND the supplied type is destroyed.
+ * this simplifies logic up the line, and is consistent with caller expecting to
+ * relinquish memory management of the type passed.
+ */
+etch_type* defvf_add_type (void* data, etch_type* type)
+{
+ default_value_factory* vf = (default_value_factory*)data;
+ etch_type *effective_type = type;
+
+ const int result = ((struct i_hashtable*)((etch_object*)vf->types)->vtab)->inserth
+ (vf->types->realtable, type, NULL, vf->types, 0);
+
+ if (-1 == result)
+ effective_type = defvf_get_type_by_name(vf, type->name);
+
+ if (effective_type != type && NULL != effective_type)
+ destroy_static_type(type);
+
+ if(effective_type == NULL)
+ {
+ ETCH_LOG("etch_default_value_factory", ETCH_LOG_ERROR, "error adding type '%s'\n", type->aname);
+ }
+ return effective_type;
+}
+
+
+/**
+ * get_idname_by_id()
+ * given a id_name map and a numeric id, return the id_name for that key.
+ * @return a non-disposable *reference*, not a copy, or null if not found.
+ */
+etch_id_name* get_idname_by_id (etch_hashtable* map, const unsigned id)
+{
+ etch_iterator iterator;
+
+ hashtable_getlock(map);
+ set_iterator(&iterator, map, &map->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ etch_id_name* this_idname = (etch_id_name*) iterator.current_key;
+ if (this_idname->id == id) {
+ hashtable_rellock(map);
+ return this_idname;
+ }
+ iterator.next(&iterator);
+ }
+ hashtable_rellock(map);
+ return NULL;
+}
+
+
+/**
+ * defvf_get_type_by_id()
+ * @return a non-disposable reference to the type matching the supplied id,
+ * or null if no match. all mixed-in vfs are recursed for a match.
+ */
+etch_type* defvf_get_type_by_id (void* data, const unsigned id)
+{
+ default_value_factory* vf = (default_value_factory*)data;
+ etch_type* type = get_idname_by_id(vf->types, id);
+ if (type) return type;
+
+ if (vf->mixins->count)
+ {
+ etch_iterator iterator;
+ set_iterator(&iterator, vf->mixins, &vf->mixins->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ default_value_factory* mvf
+ = (default_value_factory*) iterator.current_value;
+
+ if (is_etch_valuefact(mvf) && (mvf != vf))
+ {
+ type = defvf_get_type_by_id(mvf, id);
+ if (type) return type;
+ }
+
+ iterator.next(&iterator);
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * get_idname_by_name()
+ * given a id_name map and a name string, return the id_name for that name.
+ * @return a non-disposable *reference*, not a copy, or null if not found.
+ * recall that etch_type and etch_field each are typedefs of etch_id_name.
+ */
+etch_id_name* get_idname_by_name(etch_hashtable* map, const wchar_t* name)
+{
+ etch_hashitem hashbucket, *thisitem = &hashbucket;
+ const unsigned hash = etch_get_wchar_hashkey(name);
+
+ const int result = ((struct i_hashtable*)((etch_object*)map)->vtab)->findh(map->realtable, hash, map, (void**)&thisitem);
+
+ return result == 0? (etch_id_name*) thisitem->key: NULL;
+}
+
+
+/**
+ * etchtypemap_get_by_name()
+ * given a type map and a name, looks up the type matching that name,
+ * adding a new type to the map if no match was found.
+ * @return a non-disposable reference to the type matching the supplied
+ * name, or null if no match.
+ */
+etch_type* etchtypemap_get_by_name(etch_hashtable* map, const wchar_t* name)
+{
+ etch_type* thistype = get_idname_by_name(map, name);
+
+ if (NULL == thistype)
+ {
+ /* TODO handle return value */
+ ((struct i_hashtable*)((etch_object*)map)->vtab)->inserth(map->realtable, thistype = new_type(name), NULL, map, 0);
+ }
+
+ return thistype;
+}
+
+
+/**
+ * defvf_get_type_by_name()
+ * searches vf and all mixed-in vfs for type matching specified name.
+ * @return a non-disposable reference to the type corresponding to
+ * specified name, or null if no match.
+ */
+etch_type* defvf_get_type_by_name (void* data, const wchar_t* name)
+{
+ default_value_factory* vf = (default_value_factory*)data;
+ etch_type* type = get_idname_by_name(vf->types, name);
+ if (type) return type;
+
+ if (vf->mixins->count)
+ {
+ etch_iterator iterator;
+ set_iterator(&iterator, vf->mixins, &vf->mixins->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ default_value_factory* mvf
+ = (default_value_factory*) iterator.current_value;
+
+ if (is_etch_valuefact(mvf) && (mvf != vf))
+ {
+ type = defvf_get_type_by_name(mvf, name);
+ if (type) return type;
+ }
+
+ iterator.next(&iterator);
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * defvf_get_types()
+ * @return a disposable arraylist of non-disposable references to all types
+ * resident in this vf, and in all its mixed-in vfs. caller should invoke
+ * the returned list's destructor, which will destroy the list shell.
+ */
+etch_arraylist* defvf_get_types (void* data)
+{
+ default_value_factory* vf = (default_value_factory*)data;
+ etch_arraylist* typeslist = get_map_keys(vf->types);
+
+ if (vf->mixins->count)
+ {
+ int newmixintypes = 0;
+ etch_iterator iterator;
+ set_iterator(&iterator, vf->mixins, &vf->mixins->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ etch_arraylist* mvflist;
+
+ default_value_factory* mvf
+ = (default_value_factory*) iterator.current_value;
+
+ if (is_etch_valuefact(mvf) && (mvf != vf))
+ {
+ mvflist = get_map_keys(mvf->types);
+
+ newmixintypes += etch_arraylist_add_from(typeslist, mvflist, etchtypelist_comparator);
+
+ etch_object_destroy(mvflist);
+ }
+
+ iterator.next(&iterator);
+ }
+ }
+
+ return typeslist; /* caller must dispose */
+}
+
+
+/**
+ * defvf_get_string_encoding()
+ * @returns a non-disposable reference to the encoding type
+ */
+wchar_t* defvf_get_string_encoding (void* data)
+{
+ return (wchar_t*) str_utf8;
+}
+
+
+/**
+ * get_etch_string_encoding()
+ * return etch code indicating current string encoding
+ */
+int get_etch_string_encoding(etch_value_factory* vf)
+{
+ wchar_t* encoding = ((struct i_value_factory*)((etch_object*)vf)->vtab)->get_string_encoding(vf);
+ if (0 == wcscmp(encoding, L"utf-8")) return ETCH_ENCODING_UTF8;
+ if (0 == wcscmp(encoding, L"utf-16")) return ETCH_ENCODING_UTF16;
+ return ETCH_ENCODING_ASCII;
+}
+
+
+/**
+ * defvf_get_custom_struct_type()
+ * @return a non-disposable reference to the etch type type corresponding
+ * to the specified class. invoked recursively via mixed in vfs.
+ */
+etch_type* defvf_get_custom_struct_type(void* data, const unsigned etchclass)
+{
+ default_value_factory* vf = (default_value_factory*)data;
+ etch_type* type = (etch_type*) etchmap_find(vf->class_to_type, etchclass, 0);
+ if (type) return type;
+
+ if (vf->mixins->count)
+ {
+ etch_iterator iterator;
+ set_iterator(&iterator, vf->mixins, &vf->mixins->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ default_value_factory* mvf
+ = (default_value_factory*) iterator.current_value;
+
+ if (is_etch_valuefact(mvf) && (mvf != vf)) /* ensure no cycle */
+ {
+ type = ((struct i_value_factory*)((etch_object*)mvf)->vtab)->get_custom_struct_type(mvf, etchclass);
+ if (type) break;
+ }
+
+ iterator.next(&iterator);
+ }
+ }
+
+ return type;
+}
+
+
+/**
+ * defvf_import_custom_value()
+ * get helper method from the struct's type to import the custom value associated
+ * with the type. if found, invoke the method to create the custom object.
+ * @param sv the *disposable* raw key-value pairs read in by tdi,
+ * from which to reconstruct the expected custom value object.
+ * caller relinquishes ownership of this object regardless of outcome.
+ * @return the *disposable* custom object, which must be cast by caller, or null.
+ */
+etch_object* defvf_import_custom_value (void* data, etch_structvalue* sv)
+{
+ etch_object* custom_value = NULL;
+
+ etch_serializer* impxhelper = etchtype_get_impexphelper(sv->struct_type);
+
+ if (impxhelper)
+ custom_value = impxhelper->import_value(impxhelper, (etch_object*) sv);
+
+ etch_object_destroy(sv);
+
+ return custom_value;
+}
+
+
+/**
+ * defvf_export_custom_value()
+ * establishes the etch type of the custom struct to be exported to, gets the
+ * export helper method from the type, and invokes that method to do the export.
+ * @param value the custom value object to be exported to a struct for tdo.
+ * caller retains ownership of this object.
+ * @return the exported struct, or null. caller owns the returned struct.
+ */
+etch_structvalue* defvf_export_custom_value(void* data, etch_object* value)
+{
+ default_value_factory* vf = (default_value_factory*)data;
+ etch_type* custom_type = NULL;
+ etch_serializer* impxhelper = NULL;
+ etch_structvalue* exported_value = NULL;
+ if (NULL == value) return NULL;
+
+ custom_type = defvf_get_custom_struct_type(vf, ETCHOBJCLASS(value));
+
+ if (NULL == custom_type)
+ {
+ switch(((etch_object*)value)->obj_type)
+ {
+ case ETCHTYPEB_STRUCTVAL:
+ custom_type = ((etch_structvalue*)value)->struct_type;
+ break;
+ case ETCHTYPEB_EXCEPTION:
+ custom_type = builtins._mt__etch_runtime_exception;
+ break;
+ case ETCHTYPEB_ETCHLIST:
+ custom_type = builtins._mt__etch_list;
+ break;
+ case ETCHTYPEB_ETCHMAP:
+ custom_type = builtins._mt__etch_map;
+ break;
+ case ETCHTYPEB_ETCHSET:
+ custom_type = builtins._mt__etch_set;
+ break;
+ }
+ }
+
+ /* fetch non-disposable helper object from custom type */
+ impxhelper = etchtype_get_impexphelper(custom_type);
+
+ if (impxhelper && value)
+ { /* export value object to struct */
+ exported_value = (etch_structvalue*)
+ impxhelper->export_value(impxhelper, value);
+
+ /* changed to not destroy value 6/23, since the tdo itself does not own
+ * the value, and the export custom value is invoked from tdo write_value. */
+ /* value->destroy(value); */
+ }
+
+ return exported_value;
+}
+
+
+/**
+ * defvf_add_mixin()
+ * add a mixed in value factory to this value factory.
+ * @return 0 or -1.
+ */
+int defvf_add_mixin(default_value_factory* vf, etch_value_factory* mixedin_vf)
+{
+ return etch_arraylist_add(vf->mixins, mixedin_vf);
+}
+
+
+/**
+ * defvf_get_message_id()
+ * valuefactory.get_message_id() implementation.
+ * @return a non-disposable reference to the etch_int64 id object, or null.
+ */
+etch_int64* defvf_get_message_id (void* data, etch_message* msg)
+{
+ return (etch_int64*) message_get(msg, builtins._mf__message_id);
+}
+
+
+/**
+ * message_get_id32()
+ * get and return message id in 32 bits. used for debugging, logging, etc.
+ */
+unsigned message_get_id32 (etch_message* msg)
+{
+ etch_int64* id64 = (etch_int64*) message_get (msg, builtins._mf__message_id);
+ const int id32 = id64? (unsigned) id64->value: 0;
+ return id32;
+}
+
+
+/**
+ * defvf_set_message_id()
+ * valuefactory.set_message_id() implementation.
+ * @param id a *disposable* etch_int64* wrapping the message id.
+ * regardless of outcome, caller relinquishes ownership of this object.
+ * @return 0 or -1.
+ */
+int defvf_set_message_id (void* data, etch_message* msg, etch_int64* id)
+{
+ return message_put(msg, clone_field(builtins._mf__message_id), (etch_object*) id);
+}
+
+
+/**
+ * defvf_get_in_reply_to()
+ * gets the id of the message to which this message is a reply.
+ * returns a non-disposable reference to an etch_int64*, not a copy, or null.
+ */
+etch_int64* defvf_get_in_reply_to (void* data, etch_message* msg)
+{
+ return (etch_int64*) message_get(msg, builtins._mf__in_reply_to);
+}
+
+
+/**
+ * defvf_set_in_reply_to()
+ * sets the id of the message to which this message is a reply.
+ * @param id a *disposable* etch_int64* wrapping the message id.
+ * regardless of outcome, caller relinquishes ownership of this object.
+ * @return 0 or -1.
+ */
+int defvf_set_in_reply_to (void* data, etch_message* msg, etch_int64* id)
+{
+ return message_put (msg, clone_field(builtins._mf__in_reply_to), (etch_object*) id);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * vf types collection
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * defvf_typescollection_clear_handler()
+ * callback set to handle freeing of key memory during a clear() of the vf
+ * types collection. such a map owns its content, which consists of both builtin
+ * types, which are marked static, and of user types, which are generally not
+ * marked static. the type's destructor is invoked here; however the destructor
+ * will take no action on the static types (these being freed later when the
+ * builtins are destroyed. those types which are added as a result of a runtime
+ * get() call, e.g., etchtypemap_get_by_name(vf->types, L"my_newtype"),
+ * are not marked static and so will be destroyed with this map.
+ */
+int defvf_typescollection_clear_handler (void* data1, void* data2)
+{
+ #if(0)
+ etch_type* type = (etch_type*) key;
+ if (is_etchobj_static_shell(type))
+ wprintf(L"vf types map NOT destroying %s\n", type->name);
+ else wprintf(L"vf types map destroying %s\n", type->name);
+ fflush(stdout);
+ #endif
+
+ //key->destroy(key); /* see comments above re static and nonstatic types */
+ return TRUE;
+}
+
+
+/**
+ * new_vf_types_collection()
+ * return a hashtable configured as expected for a set of types
+ */
+etch_set* new_vf_types_collection(const int initialsize)
+{
+ etch_set* map = new_set(initialsize);
+ map->content_obj_type = ETCHTYPEB_TYPE;
+ map->content_class_id = CLASSID_ID_TYPE;
+ map->freehook = defvf_typescollection_clear_handler;
+ return map;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * vf_idname_map
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * defvf_idnmap_clear_handler()
+ * callback set to handle freeing of key memory during a clear() of a vf
+ * id_name map. map keys are etch_field owned by the map. however the map does
+ * not know how to destroy such a key so we handle that here. values in this
+ * map are etch_type owned elsewhere (presumably quasi-static built-in types).
+ */
+int defvf_idnmap_clear_handler (void* data1, void* data2)
+{
+ etch_object* key = (etch_object*)data1;
+ #ifdef ETCH_DEBUG_VF
+ etch_id_name* idn = (etch_id_name*) key;
+ wprintf(L"destroy idn %08x '%s'\n", (size_t) (void*) key, idn->name);
+ #endif
+
+ etch_object_destroy(key);
+ return TRUE;
+}
+
+
+/**
+ * new_vf_idname_map()
+ * return a hashtable configured as expected for a vf
+ * todo lose this method and the clear handler we don't use it
+ */
+etch_hashtable* new_vf_idname_map(const int initialsize)
+{
+ etch_hashtable* map = new_hashtable(initialsize);
+ map->is_tracked_memory = TRUE;
+ map->is_readonly_keys = FALSE; /* keys are etch_field* owned by the map */
+ map->is_readonly_values = FALSE; /* values are etch_type* owned elsewhere */
+ map->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+ map->content_obj_type = ETCHTYPEB_TYPE;
+ map->content_class_id = CLASSID_ID_TYPE;
+ map->freehook = defvf_idnmap_clear_handler; /* key memory free hook */
+ return map;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * class_to_type_map
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * class_to_type_map_put()
+ * put the specified type to the supplied map, keyed by specified class.
+ * the map does not own the specified type object.
+ * @return 0 or -1;
+ */
+int class_to_type_map_put(class_to_type_map* map, const unsigned thisclass,
+ etch_type* type)
+{
+ int hashvalue = etchmap_insert (map, thisclass, type, TRUE);
+ return hashvalue? 0: -1;
+}
+
+
+/**
+ * class_to_type_map_get()
+ * get the type associated with the specified class from the supplied map.
+ * @return a non-disposable reference to the matching type, or null.
+ */
+etch_type* class_to_type_map_get(class_to_type_map* map, const unsigned thisclass)
+{
+ etch_type* foundobj = (etch_type*) etchmap_find (map, thisclass, NULL);
+ return foundobj;
+}
+
+
+/**
+ * new_class_to_type_map()
+ * class_to_type_map constructor.
+ * such a map associates a "class", which in this context is a 32-bit unsigned
+ * integer whose high 16 bits is the etch object obj_type, and the low 16 bits
+ * the etch object class_id, to an etch_type.
+ */
+class_to_type_map* new_class_to_type_map(const int initialsize)
+{
+ /* this map calls no etch destructors when destroyed. the key strings are
+ * freed automatically by the etch_hashtable, and the values are pointers
+ * to etch_type objects owned elsewhere. it obviously follows that this
+ * map should be destroyed prior to any such referenced type object.
+ */
+ class_to_type_map* map = new_hashtable(initialsize);
+ map->is_tracked_memory = TRUE;
+ map->is_readonly_keys = TRUE; /* keys are strings owned by the hashtable */
+ map->is_readonly_values = TRUE; /* values are non-disposable etch_type* */
+ map->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+ map->content_obj_type = ETCHTYPEB_TYPE;
+ map->content_class_id = CLASSID_ID_TYPE;
+ return map;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - -
+ * built-in (etch-global, quasi-static) objects
+ * - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/* these objects are global to all vfs.
+ * for each such object, a corresponding entry should exist both in
+ * etchvf_instantiate_builtins(), and in etchvf_free_builtins()
+ */
+const wchar_t* str_etch_runtime_exception = L"_Etch_RuntimeException";
+const wchar_t* str_etch_auth_exception = L"_Etch_AuthException";
+const wchar_t* str_exception = L"_exception";
+const wchar_t* str_etch_list = L"_Etch_List";
+const wchar_t* str_etch_map = L"_Etch_Map";
+const wchar_t* str_etch_set = L"_Etch_Set";
+const wchar_t* str_etch_datetime = L"_Etch_Datetime";
+
+const wchar_t* str_msg = L"msg";
+const wchar_t* str_message_id = L"_messageId";
+const wchar_t* str_in_reply_to = L"_inReplyTo";
+const wchar_t* str_result = L"result";
+
+const wchar_t* str_utf8 = L"utf-8";
+const wchar_t* str_keys = L"keys";
+const wchar_t* str_values = L"values";
+const wchar_t* str_date_time = L"dateTime";
+const wchar_t* str_keys_values = L"keysAndValues";
+
+const wchar_t* str_msgizervf = L"Messagizer.valueFactory";
+const wchar_t* str_msgizerfmt = L"Messagizer.format";
+
+
+/*
+ * etchvf_free_builtins()
+ * frees memory for etch-global quasi-static builtin objects,
+ * and for the validators cache and its validator content.
+ * it should be invoked at etch teardown, after last vf is destroyed.
+ * unit tests will show memory leaks unless they invoke this post-test.
+ */
+
+void etchvf_free_builtins()
+{
+ if (is_builtins_instantiated)
+ {
+ destroy_static_type(builtins._mt__etch_runtime_exception);
+ destroy_static_type(builtins._mt__etch_auth_exception);
+ destroy_static_type(builtins._mt__exception);
+ destroy_static_type(builtins._mt__etch_list);
+ destroy_static_type(builtins._mt__etch_map);
+ destroy_static_type(builtins._mt__etch_set);
+ destroy_static_type(builtins._mt__etch_datetime);
+
+ destroy_static_field(builtins._mf_msg);
+ destroy_static_field(builtins._mf_result);
+ destroy_static_field(builtins._mf__message_id);
+ destroy_static_field(builtins._mf__in_reply_to);
+ }
+
+ etchvtor_clear_cache(); /* destroy cached validators */
+
+ is_builtins_instantiated = FALSE;
+}
+
+
+/**
+ * etchvf_instantiate_builtins()
+ * instantiate built-in objects such as default types and fields.
+ * these singleton objects are destroyed explicitly by invoking
+ * etchvf_free_builtins().
+ * todo: mark these objects immutable and unmark in destructor.
+ * todo: determine how we handle case of multiple vfs.
+ */
+void etchvf_instantiate_builtins()
+{
+ if (is_builtins_instantiated) return;
+
+ builtins._mt__etch_runtime_exception = new_static_type(str_etch_runtime_exception);
+ builtins._mt__etch_auth_exception = new_static_type(str_etch_auth_exception);
+ builtins._mt__exception = new_static_type(str_exception);
+ builtins._mt__etch_list = new_static_type(str_etch_list);
+ builtins._mt__etch_map = new_static_type(str_etch_map);
+ builtins._mt__etch_set = new_static_type(str_etch_set);
+ builtins._mt__etch_datetime = new_static_type(str_etch_datetime);
+
+ builtins._mf_msg = new_static_field(str_msg);
+ builtins._mf_result = new_static_field(str_result);
+ builtins._mf__message_id = new_static_field(str_message_id);
+ builtins._mf__in_reply_to = new_static_field(str_in_reply_to);
+
+ etchtype_put_validator(builtins._mt__exception,
+ clone_field(builtins._mf_result), (etch_object*) etchvtor_exception_get());
+ etchtype_put_validator(builtins._mt__exception,
+ clone_field(builtins._mf__message_id), (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(builtins._mt__exception,
+ clone_field(builtins._mf__in_reply_to),(etch_object*) etchvtor_int64_get(0));
+
+ is_builtins_instantiated = TRUE;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * other methods
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchtypelist_comparator()
+ * etch_comparator comparing two etch_types
+ * hook for arraylist_add_from to determine if a type from list b
+ * already exists in list a.
+ */
+int etchtypelist_comparator(void* a, void* b)
+{
+ return a && b && (((etch_type*)a)->id == ((etch_type*)b)->id);
+}
+
+
+/**
+ * new_vf_mixin_collection()
+ * instantiate and return a collection configured appropriately for the
+ * storage of value factory objects of mixed-in classes.
+ */
+etch_arraylist* new_vf_mixin_collection(void* data)
+{
+ default_value_factory* vf = (default_value_factory*)data;
+ //TODO: eheck if arraylist have to be synchronized
+ etch_arraylist* list = new_etch_arraylist(ETCH_DEVVF_MIXINS_DEFINITSIZE, 0);
+ list->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
+ list->is_readonly = TRUE; /* list dtor will not destroy list content */
+ list->content_obj_type = ((etch_object*)vf)->obj_type;
+ list->content_class_id = ((etch_object*)vf)->class_id;
+ return list;
+}
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_field.c b/binding-c/runtime/c/src/main/bindings/msg/etch_field.c
new file mode 100644
index 0000000..962e69b
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_field.c
@@ -0,0 +1,74 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * field.c -- methods on the etch_field object.
+ * an etch_field is an etch_id_name representing a field of a struct or message
+ * (i.e. a key for a value).
+ */
+#include "etch_field.h"
+#include "etch_objecttypes.h"
+
+/**
+ * an etch_field is for now simply a typedef of etch_id_name.
+ * all its methods except ctor are #defined as those of etch_id_name.
+ */
+
+etch_field* new_field(const wchar_t* name)
+{
+ etchparentinfo* inheritlist = NULL;
+
+ etch_field* newobj = (etch_field*) new_id_name(name);
+ if (NULL == newobj) return NULL;
+ ((etch_object*)newobj)->obj_type = ETCHTYPEB_FIELD;
+ ((etch_object*)newobj)->class_id = CLASSID_ID_FIELD;
+
+ /* fetch cached inheritance list, or create if initial instantiation,
+ * and ensure id_name parent keys exist in the (one-based) list */
+ inheritlist = get_vtab_inheritance_list((etch_object*)newobj, 2, 1, CLASSID_VTAB_FIELD);
+ ETCH_ASSERT(((etch_object*)newobj)->vtab && ((etch_object*)newobj)->vtab->inherits_from);
+ inheritlist[1].o.obj_type = ((etch_object*)newobj)->obj_type;
+ inheritlist[1].c.class_id = CLASSID_ID_NAME;
+
+ return newobj;
+}
+
+
+/**
+ * new__static_field()
+ * create a field object whose destructor will have no effect.
+ */
+etch_field* new_static_field(const wchar_t* name)
+{
+ etch_field* newfield = new_field(name);
+ set_etchobj_static_all(newfield);
+ return newfield;
+}
+
+
+/**
+ * destroy_static_field()
+ * etch_field destructor.
+ * this should not be set as the virtual dtor for a field, since the field
+ * would then not be quasi-static as desired. it should be invoked explicitly
+ */
+int destroy_static_field(etch_field* field)
+{
+ clear_etchobj_static_all(field);
+ return destroy_field(field);
+}
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_id_name.c b/binding-c/runtime/c/src/main/bindings/msg/etch_id_name.c
new file mode 100644
index 0000000..e813e32
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_id_name.c
@@ -0,0 +1,234 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * id_name.c
+ * an id_name is a base class for etch_field and etch_type, used to bind
+ * a type or field name to an associated and unique id. the id is used for
+ * certain operations, such as the key in a map, comparisons, wire encoding.
+ * it owns memory for its name, but not necessarily that for its impl.
+ * since the character name is only used internally, not on the wire,
+ * we are gradually converting this object to use an 8-bit character
+ * name, primarily to enable type-related logging.
+ */
+
+#include "etch_id_name.h"
+#include "etch_encoding.h"
+#include "etch_map.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+#include <wchar.h>
+
+/**
+ * new_id_name()
+ * constructor accepting name. id is generated from a hash of the name.
+ * allocates, initializes and returns an etch_id_name*. the specified
+ * name is cloned, thus the etch_id_name owns all its memory (note that
+ * this may not be the case for derived classes such as etch_type).
+ */
+etch_id_name* new_id_name(const wchar_t* name)
+{
+ etch_id_name* idn = NULL;
+ wchar_t* namecopy = NULL;
+ size_t bytelen = 0;
+ int result = 0;
+ if (NULL == name) return NULL;
+
+ idn = (etch_id_name*) new_object
+ (sizeof(etch_id_name), ETCHTYPEB_ID_NAME, CLASSID_ID_NAME);
+
+ ((etch_object*)idn)->destroy = destroy_id_name;
+ ((etch_object*)idn)->clone = clone_id_name;
+
+ bytelen = ( wcslen(name) + 1 ) * sizeof(wchar_t);
+ namecopy = etch_malloc(bytelen, ETCHTYPEB_BYTES);
+ /* wcscpy_s(namecopy, bytelen, name) causes a release build to hang at
+ * shutdown when the string is length 4 or more. let's not use wcscpy_s
+ * until we can determine the cause. */
+#ifdef WIN32
+ #pragma warning(disable:4996) /* disable nonsecure function warning */
+#endif
+ wcscpy(namecopy, name);
+
+ /* carrying both name versions is first step in the 8-bit conversion */
+ /* etch_unicode_to_utf8() returns us an etch_malloc'ed buffer which we own */
+ // TODO: pool
+ result = etch_encoding_transcode_wchar(&idn->aname, ETCH_ENCODING_UTF8, name, NULL);
+ ETCH_ASSERT(result != -1);
+
+ idn->name = namecopy; /* use wide name to compute hash etc. for now */
+ idn->id = compute_id_name_id_from_widename((wchar_t*)name);
+ idn->namebytelen = bytelen;
+ ((etch_object*)idn)->get_hashkey = id_name_get_hashkey;
+ ((etch_object*)idn)->get_hashkey((etch_object*)idn);
+
+ return idn;
+}
+
+
+/**
+ * new_id_name_from_8bitname()
+ * constructor accepting narrow character name.
+ * name is maintained internally as unicode.
+ * see comments at new_id_name()
+ * note that this ctor will not be needed after 8-bit conversion.
+ */
+etch_id_name* new_id_name_from_8bitname(char* name)
+{
+ etch_id_name* idn = NULL;
+ int result;
+
+ if (name)
+ {
+ wchar_t* out;
+ // TODO: pool
+ result = etch_encoding_transcode_to_wchar(&out, name, ETCH_ENCODING_UTF8, (unsigned int)strlen(name), NULL);
+ if (result == -1) {
+ return 0;
+ }
+ idn = new_id_name(out);
+ etch_free(out);
+ }
+
+ return idn;
+}
+
+
+/**
+ * clone_id_name()
+ * copy constructor
+ */
+void* clone_id_name(void* data)
+{
+ const etch_id_name* thatidn = (const etch_id_name*)data;
+ etch_id_name* newidn = thatidn? new_id_name (thatidn->name): NULL;
+
+ if (newidn)
+ {
+ ((etch_object*)newidn)->obj_type = ((etch_object*)thatidn)->obj_type;
+ ((etch_object*)newidn)->class_id = ((etch_object*)thatidn)->class_id;
+ }
+
+ return newidn;
+}
+
+
+/**
+ * destroy_id_name()
+ * destructor for an etch_id_name object.
+ * deallocates all memory allocated for the object and its contents.
+ */
+int destroy_id_name(void* data)
+{
+ etch_id_name* thisp = (etch_id_name*)data;
+
+
+ if (!is_etchobj_static_content(thisp))
+ {
+ etch_object_destroy(thisp->impl);
+
+ etch_free (thisp->name);
+ etch_free( thisp->aname);
+ }
+
+ if (!is_etchobj_static_shell(thisp))
+ etch_free(thisp);
+
+ return 0;
+}
+
+
+/**
+ * compute_id_name_id()
+ * this algorithm and its result must be identical to that of the java binding.
+ */
+int compute_id_name_id (const char* name)
+{
+ char c, *p = (char*) name;
+ int h6, i = 0, hash = 5381;
+ const int numchars = (const int) strlen(name);
+
+ for (; i < numchars; i++, p++)
+ {
+ c = *p;
+ h6 = hash << 6;
+ hash = (h6 << 10) + h6 - hash + c;
+ }
+
+ return hash;
+}
+
+
+/**
+ * compute_id_name_id_from_widename()
+ * see comments at compute_id_name_id()
+ */
+int compute_id_name_id_from_widename (const wchar_t* name)
+{
+ char *cbuf = NULL;
+ int idname_id = 0;
+ int result;
+
+ // TODO: pool
+ result = etch_encoding_transcode_wchar(&cbuf, ETCH_ENCODING_UTF8, name, NULL);
+ if (-1 == result) return -1;
+ idname_id = compute_id_name_id(cbuf);
+
+ etch_free(cbuf);
+ return idname_id;
+}
+
+
+/**
+ * is_equal_id_names()
+ */
+int is_equal_id_names (etch_id_name* thisx, etch_id_name* thatx)
+{
+ const int result = thisx && thatx
+ && (((etch_object*)thisx)->class_id == ((etch_object*)thatx)->class_id) && (thisx->id == thatx->id);
+ return result;
+}
+
+
+/**
+ * is_good_id_name()
+ * verify that the id_name is complete
+ */
+int is_good_id_name (etch_id_name* p)
+{
+ const int result = p && p->name && p->id && p->namebytelen && ((etch_object*)p)->get_hashkey(p);
+ return result;
+}
+
+
+/**
+ * id_name_get_hashkey
+ * hashkey computation for an id_name object.
+ * hash key is computed using the name string as hash source.
+ */
+uint32 id_name_get_hashkey (void* data)
+{
+ etch_object* idn = (etch_object*)data;
+ etch_id_name* thisx = (etch_id_name*) idn;
+
+ /* continue to use wide name to compute hash until it is eliminated */
+ idn->hashkey = etch_get_wchar_hashkey(thisx->name);
+
+ return idn->hashkey;
+}
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_id_name_map.c b/binding-c/runtime/c/src/main/bindings/msg/etch_id_name_map.c
new file mode 100644
index 0000000..01653b9
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_id_name_map.c
@@ -0,0 +1,57 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "etch_id_name_map.h"
+#include "etch_hash.h"
+
+struct etch_id_name_map
+{
+ etch_hashtable* ids;
+ etch_hashtable* names;
+};
+
+etch_status_t etch_id_name_map_create(etch_id_name_map** map)
+{
+ return ETCH_ENOTIMPL;
+}
+
+etch_status_t etch_id_name_map_get_by_id(etch_id_name_map* map, int32 id, void** data)
+{
+ return ETCH_ENOTIMPL;
+}
+
+etch_status_t etch_id_name_map_get_by_name(etch_id_name_map* map, const wchar_t* name, void** data)
+{
+ return ETCH_ENOTIMPL;
+}
+
+etch_status_t etch_id_name_map_add(etch_id_name_map* map, int32 id, const wchar_t* name, void* data)
+{
+ return ETCH_ENOTIMPL;
+}
+
+uint32 etch_id_name_map_count(etch_id_name_map* map)
+{
+ return ETCH_ENOTIMPL;
+}
+
+etch_status_t etch_id_name_map_destroy(etch_id_name_map* map)
+{
+ return ETCH_ENOTIMPL;
+}
+
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_message.c b/binding-c/runtime/c/src/main/bindings/msg/etch_message.c
new file mode 100644
index 0000000..93cc84a
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_message.c
@@ -0,0 +1,382 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_message.c
+ * message object
+ */
+
+#include "etch_message.h"
+#include "etch_log.h"
+#include "etch_objecttypes.h"
+
+static const char* LOG_CATEGORY = "etch_message";
+
+
+etch_message* new_message_init(etch_type*, const int);
+
+/**
+ * new_message()
+ * constructor for etch_message
+ * the value factory is a reference, the type is a copy to be passed through
+ * to the underlying struct, which will then own the type memory.
+ */
+etch_message* new_message (etch_type* type, const int size, etch_value_factory* vf)
+{
+ etch_message* newmsg = NULL;
+ if (!type || !vf) return NULL;
+
+ newmsg = new_message_init(type, size);
+ if (newmsg == NULL) return NULL;
+
+ newmsg->vf = vf;
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "creating message %x\n", newmsg);
+ return newmsg;
+}
+
+
+/**
+ * destroy_message()
+ * destructor for etch_message
+ */
+int destroy_message (void* data)
+{
+ etch_message* msg = (etch_message*)data;
+ int result = 0;
+
+ if (!is_etchobj_static_content(msg))
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "destroying message %x\n", msg);
+ result = destroy_structvalue(msg->sv);
+ }
+
+ destroy_objectex((etch_object*)msg);
+ return result;
+}
+
+/**
+ * new_message_init()
+ * common initialization on etch_message construction.
+ */
+etch_message* new_message_init(etch_type* type, const int initialsize)
+{
+ etch_message* newmsg = NULL;
+ if (NULL == type) return NULL;
+
+ newmsg = (etch_message*) new_object(sizeof(etch_message),
+ ETCHTYPEB_MESSAGE, CLASSID_ETCHMESSAGE);
+
+ ((etch_object*)newmsg)->destroy = destroy_message;
+ newmsg->sv = new_structvalue(type, initialsize);
+
+ return newmsg;
+}
+
+
+
+
+/**
+ * message_remove()
+ * removes an element from the message struct, returning the element.
+ * if the element is found, its key is destroyed, and the object is returned.
+ * caller owns returned object.
+ */
+etch_object* message_remove(etch_message* msg, etch_field* key)
+{
+ return structvalue_remove(msg->sv, key);
+}
+
+
+/**
+ * message_put()
+ * insert key/value pair
+ *
+ * @param key an etch_field whose destructor will be invoked when the struct is
+ * destroyed.
+ *
+ * @param value a *disposable* object which is the value of the key/val pair.
+ * if passed as null, specified key is removed from the message, per java binding.
+ * otherwise this object's destructor will be invoked when its struct is destroyed.
+ *
+ * @return 0 or -1.
+ * on failure, destructors are invoked here on both key and value.
+ *
+ * remarks: to simplify caller logic under normal circumstances, caller
+ * relinquishes ownership of parameters (other than this of course), regardless
+ * of outcome. should caller wish to retain ownership on failure, the parameter
+ * object could be marked such that the destructor invoked here becomes benign.
+ */
+int message_put(etch_message* msg, etch_field* key, etch_object* value)
+{
+ /* on success, struct owns key and value */
+ if (0 == structvalue_put(msg->sv, key, value))
+ return 0;
+
+ etch_object_destroy(value);
+ value = NULL;
+
+ destroy_field(key); /* note we are often passed protected vf keys */
+ return -1;
+}
+
+
+/**
+ * message_putc()
+ * insert key/value pair and clear value reference
+ *
+ * @param key an etch_field whose destructor will be invoked when the struct is
+ * destroyed.
+ *
+ * @param value an indirect reference to a *disposable* object which is the value
+ * of the key/val pair.
+ * caller's reference is nulled out once this object is relinquished; thus caller
+ * can safely test for null and then destroy the object.
+ *
+ * @return 0 or -1.
+ * on failure, destructors are invoked here on both key and value.
+ *
+ * remarks: to simplify caller logic under normal circumstances, caller
+ * relinquishes ownership of parameters (other than this of course), regardless
+ * of outcome. should caller wish to retain ownership on failure, the parameter
+ * object could be marked such that the destructor invoked here becomes benign.
+ */
+int message_putc(etch_message* msg, etch_field* key, void** valref)
+{
+ etch_object* value = NULL;
+
+ if (valref)
+ { value = (etch_object*) *valref;
+ *valref = NULL; /* clear caller's reference as we now own value */
+ }
+
+ if (value){ /* on success, struct owns key and value */
+ if (0 == structvalue_put (msg->sv, key, value)){
+ return 0;
+ }
+ }
+
+ /* a struct put was not successful so destroy parameter objects */
+ etch_object_destroy(value);
+ value = NULL;
+
+ destroy_field(key); /* note we are often passed protected vf keys */
+ return -1;
+}
+
+
+/**
+ * message_get()
+ * fetch value for key.
+ * @return a non-disposable reference to the value, not a copy, or null.
+ */
+etch_object* message_get(etch_message* msg, etch_field* key)
+{
+ etch_object* retobj = NULL;
+
+ if (msg && key && msg->sv)
+ retobj = structvalue_get(msg->sv, key);
+
+ #if(0)
+ if (NULL == msg || NULL == key || NULL == msg->sv)
+ return throw_from(EXCPTYPE_ILLEGALARG, ETCHTYPEB_UNDEFINED, 0, 0);
+ retobj = structvalue_get(msg->sv, key);
+ #endif
+
+ return retobj;
+}
+
+
+/**
+ * message_type()
+ * return etch type of specified message
+ */
+etch_type* message_type(etch_message* msg)
+{
+ return msg && msg->sv? msg->sv->struct_type: NULL;
+}
+
+
+/**
+ * message_aname()
+ * return name of specified message in 8-bit encoding.
+ * @return a reference to the message name, caller does not own it.
+ */
+char* message_aname (etch_message* msg)
+{
+ etch_type* msgtype = message_type(msg);
+ char* msgname = msgtype && msgtype->aname? msgtype->aname: "";
+ return msgname;
+}
+
+
+/**
+ * message_reply()
+ * creates a message which is a reply to the current message.
+ * the current message's value factory is copied to the new message.
+ * message id of the current message (if any) is copied into the
+ * in-reply-to field of the new message.
+ * @param newtype the type of the reply. caller retains ownership.
+ * @return a reply message, which will contain exception if error.
+ */
+etch_message* message_reply (etch_message* msg, etch_type* newtype)
+{
+ int result = -1;
+ etch_int64* msgid = NULL;
+ etch_message* newmsg = NULL;
+
+ if (NULL == msg) return NULL;
+
+ if (NULL == newtype) /* use message type's result type */
+ newtype = etchtype_get_result_type (msg->sv->struct_type);
+
+ if (NULL == newtype) return NULL;
+
+ /* construct message. caller retains ownership of type */
+ newmsg = new_message (newtype, 0, msg->vf);
+
+ msgid = message_get_id (msg); /* get back a ref to ID or null */
+
+ if (msgid)
+ result = message_set_in_reply_to (newmsg, ((etch_object*)msgid)->clone(msgid));
+
+ if (0 != result)
+ {
+ etch_object_destroy(newmsg);
+ newmsg = NULL;
+ }
+
+ return newmsg;
+}
+
+
+/**
+ * message_set_id()
+ * sets the message-id field of this message.
+ * @param id a *disposable* long object wrapping the connection specific
+ * unique identifier of this message, or NULL if the message has not yet
+ * been sent. NOTE that the send process overwrites any value which might
+ * otherwise be set here.
+ */
+int message_set_id(etch_message* msg, etch_int64* id)
+{
+ int result = 0;
+ if (!id) return -1;
+
+ /* id object ownership is relinquished here even if the call fails */
+ result = ((struct i_value_factory*)((etch_object*)msg->vf)->vtab)->set_message_id(msg->vf, msg, id);
+ return result;
+}
+
+
+/**
+ * message_get_id()
+ * @return a non-disposable reference to the connection specific unique
+ * identifier of this message, or null if there was no such identifier.
+ */
+etch_int64* message_get_id(etch_message* msg)
+{
+ etch_int64* id = ((struct i_value_factory*)((etch_object*)msg->vf)->vtab)->get_message_id(msg->vf, msg);
+ return id;
+}
+
+
+/**
+ * message_get_in_reply_to()
+ * @return a non-disposable reference to the message-id of the message that
+ * this message is a response to, or null if this is an original message
+ * or if the original message did not have a message-id.
+ * caller does not own the returned object.
+ */
+etch_int64* message_get_in_reply_to(etch_message* msg)
+{
+ /* vf returns to us a reference to its value */
+ etch_int64* id = ((struct i_value_factory*)((etch_object*)msg->vf)->vtab)->get_in_reply_to(msg->vf, msg);
+ return id;
+}
+
+
+/**
+ * message_set_in_reply_to()
+ * sets the in-reply-to field of this message.
+ * @param msgid a *disposable* long object wrapping the message-id of the
+ * message that this message is a response to. note that caller must clone
+ * or otherwise supply a disposable object as this parameter.
+ */
+int message_set_in_reply_to(etch_message* msg, etch_int64* msgid)
+{
+ int result = 0;
+ if (!msgid) return -1;
+
+ /* msgid ownership is relinquished here even if the call fails */
+ result = ((struct i_value_factory*)((etch_object*)msg->vf)->vtab)->set_in_reply_to(msg->vf, msg, msgid);
+ return result;
+}
+
+
+/**
+ * message_size()
+ */
+
+int message_size (etch_message* msg)
+{
+ return msg? structvalue_count(msg->sv): 0;
+}
+
+
+/* - - - - - - - - - - -
+ * etch_unwanted_message
+ * - - - - - - - - - - -
+ */
+
+/**
+ * destroy_unwanted_message()
+ * etch_unwanted_message destructor
+ */
+int destroy_unwanted_message(void* data)
+{
+ etch_unwanted_message* msg = (etch_unwanted_message*)data;
+
+ if (msg->message != NULL && !is_etchobj_static_content(msg))
+ {
+ msg->message->sv->items->is_readonly_keys = 0;
+ msg->message->sv->items->is_readonly_values = 0;
+ etch_object_destroy(msg->message);
+ }
+
+ return destroy_objectex((etch_object*) msg);
+}
+
+
+/**
+ * new_unwanted_message()
+ * etch_unwanted_message constructor
+ * @param whofrom caller retains
+ * @param msg caller relinquishes
+ */
+etch_unwanted_message* new_unwanted_message(etch_who* whofrom, etch_message* msg)
+{
+ etch_unwanted_message* newmsg = (etch_unwanted_message*) new_object
+ (sizeof(etch_unwanted_message), ETCHTYPEB_EVENT, CLASSID_EVENT_UNWANTMSG);
+
+ ((etch_object*)newmsg)->destroy = destroy_unwanted_message;
+ ((etch_object*)newmsg)->clone = clone_null;
+
+ newmsg->message = msg;
+ newmsg->whofrom = whofrom;
+ return newmsg;
+}
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_structval.c b/binding-c/runtime/c/src/main/bindings/msg/etch_structval.c
new file mode 100644
index 0000000..ca0790c
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_structval.c
@@ -0,0 +1,256 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_structval.c -- etch_structvalue implementation.
+ */
+
+#include "etch_runtime.h"
+#include "etch_structval.h"
+#include "etch_validator.h"
+#include "etch_encoding.h"
+#include "etch_exception.h"
+#include "etch_map.h"
+#include "etch_log.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+
+char* ETCHSVAL = "SVAL";
+
+etch_structvalue* new_structvalue_init();
+
+/*
+ * structvalue_clear_handler()
+ * this callback is set to handle freeing of key and value memory during a clear()
+ * of the structvalue map. structs own all their memory, so if there is a problem
+ * here, it should be resolved at the source, not by modifying this code.
+ */
+int structvalue_clear_handler (void* data1, void* data2)
+{
+ etch_field* key = (etch_field*)data1;
+ etch_object* value = (etch_object*)data2;
+ etch_object_destroy(value);
+ destroy_field(key);
+ return TRUE;
+}
+
+
+
+/**
+ * new_structvalue()
+ * primary constructor for etch_structvalue.
+ * @param etch_type object, caller retains ownership of the type as usual
+ */
+etch_structvalue* new_structvalue(etch_type* type, const int initialsize)
+{
+ etch_structvalue* newobj = new_structvalue_init(initialsize);
+
+ newobj->struct_type = type;
+
+ return newobj;
+}
+
+
+/**
+ * destroy_structvalue()
+ * destructor for an etch_structvalue object
+ *
+ * a structvalue owns all its memory *except* its type object, which is global
+ * to the vf. this means that (a) the etch_type supplied on construction must be
+ * a reference to a type owned by the service vf (or the unit test); and (b) all
+ * struct keys must be etch_field* allocated on the heap and not referenced again
+ * outside of that structvalue scope; and (c) all struct values must be etch object
+ * references allocated on the heap and not referenced again outside of the
+ * structvalue scope.
+ */
+int destroy_structvalue(void* data)
+{
+ etch_structvalue* thisp = (etch_structvalue*)data;
+ if (NULL == thisp) return 0;
+
+
+ if (!is_etchobj_static_content(thisp)){
+ etch_object_destroy(thisp->items);
+ thisp->items = NULL;
+ }
+
+ /* see comments above as to why we don't destroy type */
+
+ destroy_objectex((etch_object*)thisp);
+ return 0;
+}
+
+
+/**
+ * new_structvalue_init() (private)
+ * common initialization on etch_structvalue construction.
+ */
+etch_structvalue* new_structvalue_init(const int initialsize)
+{
+ etch_structvalue* newobj = etch_malloc(sizeof(etch_structvalue), ETCHTYPEB_STRUCTVAL);
+ memset(newobj, 0, sizeof(etch_structvalue));
+ ((etch_object*)newobj)->obj_type = ETCHTYPEB_STRUCTVAL;
+ ((etch_object*)newobj)->class_id = CLASSID_STRUCTVALUE; /* for now anyway */
+
+ ((etch_object*)newobj)->destroy = destroy_structvalue;
+ ((etch_object*)newobj)->clone = clone_null;
+
+ newobj->items = new_structvalue_hashtable(initialsize);
+ /* mark map such that it knows its keys and values are etch objects */
+ newobj->items->content_type = ETCHHASHTABLE_CONTENT_OBJECT_OBJECT;
+
+ return newobj;
+}
+
+
+
+
+
+/*
+ * new_structvalue_hashtable
+ * create the backing store for a struct value
+ */
+etch_hashtable* new_structvalue_hashtable(const int initialsize)
+{
+ etch_hashtable* ht = new_hashtable(initialsize);
+ if (ht == NULL) return NULL;
+ ht->content_type = ETCH_STRUCT_DEFAULT_CONTENT_TYPE;
+ ht->is_tracked_memory = ETCH_STRUCT_DEFAULT_TRACKED_MEM;
+ ht->is_readonly_keys = ETCH_STRUCT_DEFAULT_READONLY_KEY;
+ ht->is_readonly_values = ETCH_STRUCT_DEFAULT_READONLY_VAL;
+ ht->freehook = structvalue_clear_handler;
+ return ht;
+}
+
+
+/**
+ * structvalue_put()
+ * inserts (or removes) specified key/value pair to/from struct store.
+ * @param key an etch_field whose destructor will be invoked when the struct is
+ * destroyed. presumably this etch_field is disposable; if not, the object must
+ * be marked as immutable using set_etchobj_static_all.
+ * @param value a *disposable* object which is the value of the key/val pair.
+ * this object's destructor will be invoked when the struct is destroyed.
+ * presumably this object and its content are disposable; if not, the object must
+ * be marked accordingly using set_etchobj_static_all or set_etchobj_static_content.
+ * returns 0 or -1.
+ */
+int structvalue_put(etch_structvalue* thisp, etch_field* key, etch_object* value)
+{
+ etch_config_t* config = NULL;
+ int32 propvalue = 0;
+ etch_hashtable* map = NULL;
+
+ etch_runtime_get_config(&config);
+ ETCH_ASSERT(config);
+
+ map = thisp->items;
+ if (NULL == key) return -1;
+
+ if (NULL == value) /* per contract, no value implies removal desired */
+ return ((struct i_hashtable*)((etch_object*)map)->vtab)->removeh (map->realtable, ((etch_object*)key)->get_hashkey(key), key, 0);
+
+ etch_config_get_property_int(config, "etch.validate.write", &propvalue);
+ if(propvalue == 1) {
+ etch_type* thistype = thisp->struct_type;
+ char *errmsg = NULL;
+
+ etch_validator* vtor = (etch_validator*)
+ etchtype_get_validator_by_name(thistype, key->name);
+
+ if (NULL == vtor)
+ errmsg = "validator missing";
+ else
+ if (0 != vtor->validate (vtor, (etch_object*) value))
+ errmsg = "validation failed";
+
+ if (errmsg)
+ { ETCH_LOG(ETCHSVAL, ETCH_LOG_ERROR, "%s for type '%s' field '%s'\n", errmsg, thistype->aname, key->aname);
+ return -1;
+ }
+ }
+ return ((struct i_hashtable*)((etch_object*)map)->vtab)->inserth (map->realtable, key, value, map, 0);
+}
+
+
+/**
+ * structvalue_get()
+ * access an element from the struct.
+ * returns a reference not a copy.
+ */
+etch_object* structvalue_get (etch_structvalue* thisp, etch_field* key)
+{
+ int result = 0;
+ etch_hashitem hashbucket;
+ etch_hashitem* thisitem = &hashbucket;
+ etch_hashtable* map = thisp? thisp->items: NULL;
+ if (NULL == map) return NULL;
+
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->findh(map->realtable, ((etch_object*)key)->get_hashkey(key), map, (void**)&thisitem);
+
+ return result == 0? thisitem->value: NULL;
+}
+
+
+/**
+ * structvalue_remove
+ * removes an element from the struct, returning the element.
+ * if the element is found, its key is destroyed, and the object is returned.
+ * caller owns returned object.
+ */
+etch_object* structvalue_remove(etch_structvalue* thisp, etch_field* key)
+{
+ int result = 0;
+ etch_hashitem hashbucket;
+ etch_hashitem* thisitem = &hashbucket;
+ etch_hashtable* map = thisp? thisp->items: NULL;
+ memset(thisitem,0,sizeof(etch_hashitem));
+ if (NULL == map) return NULL;
+
+ /* remove specified item from hashtable without destroying content */
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->removeh(map->realtable, ((etch_object*)key)->get_hashkey(key), map, (void**)&thisitem);
+ if (-1 == result) return NULL;
+
+ /* free entry key */
+ if (thisitem->key)
+ if (etchmap_is_object_key(map))
+ ((etch_object*)thisitem->key)->destroy(thisitem->key);
+ else etch_free(thisitem->key);
+
+ return (etch_object*) thisitem->value;
+}
+
+
+/**
+ * structvalue_is_type()
+ * indicates if type of this struct is the same as the specified type
+ */
+int structvalue_is_type(etch_structvalue* thisp, etch_type* type)
+{
+ return is_equal_types(thisp->struct_type, type);
+}
+
+
+/**
+ * structvalue_count()
+ * returns number of pairs in the struct
+ */
+int structvalue_count(etch_structvalue* sv)
+{
+ return etchmap_count(sv->items);
+}
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_tagdata.c b/binding-c/runtime/c/src/main/bindings/msg/etch_tagdata.c
new file mode 100644
index 0000000..5029300
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_tagdata.c
@@ -0,0 +1,604 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_tagdata.c -- tagged data implementation.
+ */
+
+#include "etch_tagged_data.h"
+#include "etch_type.h"
+#include "etch_arrayval.h"
+#include "etch_nativearray.h"
+#include "etch_objecttypes.h"
+
+
+/* the only instance data in the java version is the value factory
+ * for C, inheritors can implement that, in order that we don't need
+ * any instance data here and can just implement methods.
+ */
+
+/*
+ * etchtagdata_get_number()
+ * java.Lang.Number emulation support.
+ * given an anonymous wrapped primitive, return its value represented as
+ * a 64-bit integer.
+ */
+int etchtagdata_get_number(etch_object* valobj,
+ const double fmin, const double fmax, int64* out)
+{
+ int64 longval = 0;
+ double dval;
+ if (!is_etch_primitive_number(valobj)) return -1;
+
+ switch(((etch_object*)valobj)->class_id)
+ {
+ case CLASSID_PRIMITIVE_INT32:
+ longval = ((etch_int32*)valobj)->value;
+ break;
+
+ case CLASSID_PRIMITIVE_INT64:
+ longval = ((etch_int64*)valobj)->value;
+ break;
+
+ case CLASSID_PRIMITIVE_BYTE:
+ case CLASSID_PRIMITIVE_BOOL:
+ case CLASSID_PRIMITIVE_INT8:
+ longval = ((etch_byte*)valobj)->value;
+ break;
+
+ case CLASSID_PRIMITIVE_INT16:
+ longval = ((etch_int16*)valobj)->value;
+ break;
+
+ case CLASSID_PRIMITIVE_DOUBLE:
+ case CLASSID_PRIMITIVE_FLOAT:
+ {
+ switch(((etch_object*)valobj)->class_id)
+ { case CLASSID_PRIMITIVE_DOUBLE:
+ dval = ((etch_double*)valobj)->value;
+ break;
+ case CLASSID_PRIMITIVE_FLOAT:
+ dval = (double) ((etch_float*)valobj)->value;
+ break;
+ }
+
+ /* round ieee value if necessary */
+ if (dval >= 0.0 && dval <= fmax)
+ dval += 0.5;
+ else
+ if (dval < 0.0 && dval >= fmin)
+ dval -= 0.5;
+
+ longval = (int64) dval;
+ break;
+ }
+ }
+
+ *out = longval;
+ return 0;
+}
+
+
+/*
+ * etchtagdata_get_fnumber()
+ * java.Lang.Number emulation support.
+ * given an anonymous wrapped primitive, return its value represented as
+ * an IEEE number of 64 bits.
+ */
+int etchtagdata_get_double_number(etch_object* valobj,
+ const double fmin, const double fmax, double* outd)
+{
+ float fval = 0.0; double dval = 0.0; int64 longval = 0;
+
+ if (-1 == etchtagdata_get_number(valobj, fmin, fmax, &longval))
+ return -1;
+
+ switch(((etch_object*)valobj)->class_id)
+ {
+ case CLASSID_PRIMITIVE_DOUBLE:
+ dval = ((etch_double*)valobj)->value;
+ break;
+
+ case CLASSID_PRIMITIVE_FLOAT:
+ fval = ((etch_float*)valobj)->value;
+ dval = (double) fval;
+ break;
+
+ default:
+ dval = (double) longval;
+ }
+
+ *outd = dval;
+ return 0;
+}
+
+
+/*
+ * etchtagdata_get_float_number()
+ * java.Lang.Number emulation support.
+ * given an anonymous wrapped primitive, return its value represented as
+ * an IEEE number of 32 bits.
+ */
+int etchtagdata_get_float_number(etch_object* valobj,
+ const double fmin, const double fmax, float* outf)
+{
+ float fval = 0.0; double dval = 0.0; int64 longval = 0;
+
+ if (-1 == etchtagdata_get_number(valobj, fmin, fmax, &longval))
+ return -1;
+
+ switch(((etch_object*)valobj)->class_id)
+ {
+ case CLASSID_PRIMITIVE_DOUBLE:
+ dval = ((etch_double*)valobj)->value;
+ fval = (float) dval;
+ break;
+
+ case CLASSID_PRIMITIVE_FLOAT:
+ fval = ((etch_float*)valobj)->value;
+ break;
+
+ default:
+ fval = (float) longval;
+ }
+
+ *outf = fval;
+ return 0;
+}
+
+
+/*
+ * etchtagdata_byte_value()
+ * mimic java.Lang.Number.byteValue()
+ */
+int etchtagdata_byte_value(etch_object* valobj, byte* out)
+{
+ int64 longval = 0;
+
+ const int result = etchtagdata_get_number(valobj, 0, 0, &longval);
+
+ if (0 == result && out)
+ (*out) = (byte) longval; /* truncate and return value */
+
+ return result;
+}
+
+
+/*
+ * etchtagdata_bool_value()
+ * if there was a java.Lang.Number.booleanValue(), this would mimic it
+ */
+int etchtagdata_bool_value(etch_object* valobj, boolean* out)
+{
+ int64 longval = 0;
+
+ const int result = etchtagdata_get_number(valobj, 0, 0, &longval);
+
+ if (0 == result && out)
+ {
+ if (longval != 1) {
+ *out = 1;
+ }
+ else {
+ *out = 0;
+ }
+ }
+
+ return result;
+}
+
+
+/*
+ * etchtagdata_int16_value()
+ * mimic java.Lang.Number.shortValue()
+ */
+int etchtagdata_int16_value(etch_object* valobj, short* out)
+{
+ int64 longval = 0;
+
+ const int result = etchtagdata_get_number(valobj, 0, 0, &longval);
+
+ if (0 == result && out)
+ (*out) = (short) longval; /* truncate and return value */
+
+ return result;
+}
+
+
+/*
+ * etchtagdata_int32_value()
+ * mimic java.Lang.Number.intValue()
+ */
+int etchtagdata_int32_value(etch_object* valobj, int* out)
+{
+ int64 longval = 0;
+
+ const int result = etchtagdata_get_number(valobj, 0, 0, &longval);
+
+ if (0 == result && out)
+ (*out) = (int) longval; /* truncate and return value */
+
+ return result;
+}
+
+
+/*
+ * etchtagdata_int64_value()
+ * mimic java.Lang.Number.longValue()
+ */
+int etchtagdata_int64_value(etch_object* valobj, int64* out)
+{
+ int64 longval = 0;
+
+ const int result = etchtagdata_get_number(valobj, 0, 0, &longval);
+
+ if (0 == result && out)
+ (*out) = longval;
+
+ return result;
+}
+
+
+/*
+ * etchtagdata_float_value()
+ * mimic java.Lang.Number.floatValue()
+ */
+int etchtagdata_float_value(etch_object* valobj, float* out)
+{
+ const static double ETCHTYPE_MAX_FLOATF = ETCHTYPE_MAX_FLOAT + 0.4999;
+ const static double ETCHTYPE_MIN_FLOATF = ETCHTYPE_MIN_FLOAT - 0.4999;
+ float floatval = 0.0;
+
+ const int result = etchtagdata_get_float_number(valobj,
+ ETCHTYPE_MIN_FLOATF, ETCHTYPE_MAX_FLOATF, &floatval);
+
+ if (0 == result && out)
+ (*out) = floatval;
+
+ return result;
+}
+
+
+/*
+ * etchtagdata_double_value()
+ * mimic java.Lang.Number.doubleValue()
+ */
+int etchtagdata_double_value(etch_object* valobj, double* out)
+{
+ double dval = 0.0;
+
+ const int result = etchtagdata_get_double_number(valobj,
+ ETCHTYPE_MIN_DOUBLE, ETCHTYPE_MAX_DOUBLE, &dval);
+
+ if (0 == result && out)
+ (*out) = dval;
+
+ return result;
+}
+
+
+/*
+ * etchtagdata_adjust_tiny_int()
+ * if target value is one byte and value is in range of tiny int,
+ * adjust the type code accordingly, returning it in out parameter
+ */
+int etchtagdata_adjust_tiny_int(const signed char target_type, etch_object* valobj, signed char* out)
+{
+ signed char type_out = target_type;
+ signed char byteval = 0;
+ int result = 0;
+
+ if (target_type == ETCH_XTRNL_TYPECODE_BYTE) {
+ if (0 == (result = etchtagdata_byte_value(valobj, (byte*)&byteval))) {
+ if (is_inrange_tiny_for_signed_chars(byteval)) {
+ type_out = byteval;
+ }
+ }
+ }
+ if (out) {
+ (*out = type_out);
+ }
+ return result;
+}
+
+
+/*
+ * etchtagdata_validate_value()
+ * in java binding this is checkValue(object, validator);
+ * defers to validator to determine type code for the value.
+ * adjusts the type to tiny integer if applicable and possible.
+ * returns the type code in the out parameter.
+ */
+int etchtagdata_validate_value(etch_object* valobj, etch_validator* vtor, signed char* out)
+{
+ signed char xtype = 0;
+ int result = 0;
+
+ if (NULL == valobj) {
+ xtype = ETCH_XTRNL_TYPECODE_NULL;
+ } else {
+ if (etchtagdata_is_eod(valobj)) {
+ xtype = ETCH_XTRNL_TYPECODE_NONE;
+ } else {
+ if (0 == (result = vtor->check_value(vtor, valobj, (byte*)&xtype))) {
+ result = etchtagdata_adjust_tiny_int(xtype, valobj, &xtype);
+ }
+ }
+ }
+ if (out) {
+ (*out = xtype);
+ }
+ return result;
+}
+
+
+/**
+ * etchtagdata_check_value()
+ * returns a type code for the specified value
+ * @param valobj an etch value object, caller retains ownership.
+ */
+signed char etchtagdata_check_value (etch_object* valobj)
+{
+ signed char xtype = 0;
+ const unsigned int obj_type = valobj? ((etch_object*)valobj)->obj_type: 0;
+ const unsigned int class_id = valobj? ((etch_object*)valobj)->class_id: 0;
+
+ if (NULL == valobj)
+ xtype = ETCH_XTRNL_TYPECODE_NULL;
+ else
+ if (etchtagdata_is_eod(valobj))
+ xtype = ETCH_XTRNL_TYPECODE_NONE;
+ else
+ if (is_etch_primitive(valobj))
+ {
+ switch(class_id)
+ {
+ case CLASSID_PRIMITIVE_INT32:
+ xtype = etchtagdata_check_integer(((etch_int32*)valobj)->value);
+ break;
+ case CLASSID_PRIMITIVE_INT16:
+ xtype = etchtagdata_check_short(((etch_int16*)valobj)->value);
+ break;
+ case CLASSID_PRIMITIVE_INT64:
+ xtype = etchtagdata_check_long(((etch_int64*)valobj)->value);
+ break;
+ case CLASSID_PRIMITIVE_BYTE:
+ case CLASSID_PRIMITIVE_INT8:
+ xtype = etchtagdata_check_byte(((etch_byte*)valobj)->value);
+ break;
+ case CLASSID_PRIMITIVE_DOUBLE:
+ xtype = ETCH_XTRNL_TYPECODE_DOUBLE;
+ break;
+ case CLASSID_PRIMITIVE_FLOAT:
+ xtype = ETCH_XTRNL_TYPECODE_FLOAT;
+ break;
+ case CLASSID_PRIMITIVE_BOOL:
+ xtype = ((etch_boolean*)valobj)->value?
+ ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE:
+ ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE;
+ break;
+ case CLASSID_STRING:
+ xtype = ((etch_string*)valobj)->char_count == 0?
+ ETCH_XTRNL_TYPECODE_EMPTY_STRING:
+ ETCH_XTRNL_TYPECODE_STRING;
+ break;
+ default:
+ xtype = ETCH_XTRNL_TYPECODE_CUSTOM;
+ break;
+ } /* switch(class_id) */
+ }
+ else switch(obj_type)
+ {
+ case ETCHTYPEB_STRUCTVAL:
+ xtype = ETCH_XTRNL_TYPECODE_CUSTOM;
+ break;
+ case ETCHTYPEB_ARRAYVAL:
+ xtype = ((etch_arrayvalue*)valobj)->content_obj_type == ETCHTYPEB_BYTE?
+ ETCH_XTRNL_TYPECODE_BYTES: ETCH_XTRNL_TYPECODE_ARRAY;
+ break;
+ case ETCHTYPEB_NATIVEARRAY:
+ if(((etch_nativearray*)valobj)->numdims > 1) {
+ xtype = ETCH_XTRNL_TYPECODE_ARRAY;
+ break;
+ }
+ xtype = ((etch_nativearray*)valobj)->content_obj_type == ETCHTYPEB_BYTE?
+ ETCH_XTRNL_TYPECODE_BYTES: ETCH_XTRNL_TYPECODE_ARRAY;
+ break;
+ default:
+ xtype = ETCH_XTRNL_TYPECODE_CUSTOM;
+ }
+
+ return xtype;
+}
+
+
+/*
+ * etchtagdata_get_native_typecode()
+ * returns the external type code corresponding to internal type.
+ * see etch_binary_tdi.c for implementation
+ */
+byte etchtagdata_get_native_typecode
+ (const unsigned short obj_type, const unsigned short class_id)
+{
+ return ETCH_XTRNL_TYPECODE_NONE;
+}
+
+
+/*
+ * etchtagdata_get_native_type()
+ * returns the internal type and class ids corresponding to external typecode.
+ * see etch_binary_tdi.c for implementation
+ */
+unsigned etchtagdata_get_native_type (const byte typecode)
+{
+ return (CLASSID_NONE << 16) | ETCHTYPEB_NONE;
+}
+
+
+/*
+ * etchtagdata_get_custom_structtype()
+ * returns a struct type for the specified class
+ * see etch_binary_tdi for implementation
+ */
+etch_type* etchtagdata_get_custom_structtype (etch_object* thisx,
+ const unsigned short obj_type, const unsigned short class_id)
+{
+ /* we will return a non-disposable (static) type */
+ etch_type* type = NULL;
+ return type;
+}
+
+
+
+
+/**
+ * etchtagdata_from_arrayvalue()
+ * return an etch_nativearray representation of the specified etch_arrayvalue.
+ * note that an arrayvalue, if created from an etch_nativearray, has retained
+ * the source nativearray as an instance member. is_force indicates if caller
+ * wants to build the nativearray regardless of whether one currently exists.
+ */
+etch_nativearray* etchtagdata_from_arrayvalue(etch_arrayvalue* av, const int is_force)
+{
+ if (av->natarray && !is_force) return av->natarray;
+ return (-1 == arrayvalue_to_nativearray(av))? NULL: av->natarray;
+}
+
+
+/**
+ * etchtagdata_alloc_arrayvalue()
+ * creates and returns an empty arrayvalue object. this method is here for
+ * for compatibility with the java binding, however it is superfluous.
+ * we can safely substitute the new_arrayvalue call below and lose this method.
+ */
+etch_arrayvalue* etchtagdata_alloc_arrayvalue(const byte type_code,
+ etch_type* custom_struct_type, const int dim, const int initsize)
+{
+ return new_arrayvalue(type_code, custom_struct_type, dim,
+ initsize, initsize, FALSE, ETCHARRAYLIST_SYNCHRONIZED);
+}
+
+
+/*
+ * etchtagdata_check_byte()
+ */
+signed char etchtagdata_check_byte(const signed char val)
+{
+ byte result = ETCH_XTRNL_TYPECODE_BYTE;
+ if (is_inrange_tiny_for_signed_chars(val))
+ result = val;
+ return result;
+}
+
+
+/*
+ * etchtagdata_check_short()
+ */
+signed char etchtagdata_check_short(const short val)
+{
+ byte result = ETCH_XTRNL_TYPECODE_SHORT;
+ if (is_inrange_byte(val))
+ result = etchtagdata_check_byte((byte)val);
+ return result;
+}
+
+
+/*
+ * etchtagdata_check_integer()
+ */
+signed char etchtagdata_check_integer(const int val)
+{
+ byte result = ETCH_XTRNL_TYPECODE_INT;
+ if (is_inrange_int16(val))
+ result = etchtagdata_check_short((short)val);
+ return result;
+}
+
+
+/*
+ * etchtagdata_check_long()
+ */
+signed char etchtagdata_check_long(const int64 val)
+{
+ byte result = ETCH_XTRNL_TYPECODE_LONG;
+ if (is_inrange_int32(val))
+ result = etchtagdata_check_integer((int)val);
+ return result;
+}
+
+
+/**
+ * etchtagdata_is_null()
+ * indicate whether this object represents a null value
+ */
+int etchtagdata_is_null(etch_object* obj)
+{
+ return obj == NULL || obj->is_null;
+}
+
+
+/**
+ * etchtagdata_is_eod()
+ * indicate whether this object is the end of stream sentinel
+ */
+int etchtagdata_is_eod(etch_object* obj)
+{
+ return obj && ((etch_object*)obj)->obj_type == ETCHTYPEB_EODMARK;
+}
+
+
+/**
+ * etchtagdata_new_nullobj()
+ * instantiate and return a logically null object
+ */
+etch_object* etchtagdata_new_nullobj(const int is_static)
+{
+ etch_object* obj = new_nullobj();
+ if (is_static) set_etchobj_static_all(obj);
+ return obj;
+}
+
+
+/**
+ * etchtagdata_new_eodmarker()
+ * instantiate and return an end of data marker object
+ */
+etch_object* etchtagdata_new_eodmarker(const int is_static)
+{
+ etch_object* obj = new_object(sizeof(etch_object), ETCHTYPEB_EODMARK, CLASSID_NONE);
+ if (is_static) set_etchobj_static_all(obj);
+ return obj;
+}
+
+
+/**
+ * etchtagdata_new_emptystring()
+ * instantiate and return an empty string object
+ */
+etch_string* etchtagdata_new_emptystring(const int is_static)
+{
+ etch_string* obj = new_stringw(L"");
+ if (is_static) set_etchobj_static_all(obj);
+ return obj;
+}
+
+
+
+
+
+
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_tagdata_inp.c b/binding-c/runtime/c/src/main/bindings/msg/etch_tagdata_inp.c
new file mode 100644
index 0000000..363d94c
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_tagdata_inp.c
@@ -0,0 +1,185 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_tagdata_inp.c -- tagged_data_input implementation.
+ */
+
+#include "etch_tagdata_inp.h"
+#include "etch_cache.h"
+#include "etch_message.h"
+#include "etch_flexbuffer.h"
+#include "etch_arrayval.h"
+#include "etch_objecttypes.h"
+/*
+static const char* LOG_CATEGORY = "etch_tdi";
+*/
+/**
+ * tdi_start_message()
+ */
+etch_message* tdi_start_message(tagged_data_input* tdi)
+{
+ return NULL;
+}
+
+
+etch_message* tdi_read_message(tagged_data_input* tdi, etch_flexbuffer* f)
+{
+ return NULL;
+}
+
+
+/**
+ * tdi_end_message()
+ */
+int tdi_end_message(tagged_data_input* tdi, etch_message* msg)
+{
+ return NULL;
+}
+
+
+/**
+ * tdi_start_struct()
+ */
+etch_structvalue* tdi_start_struct(tagged_data_input* tdi)
+{
+ return NULL;
+}
+
+
+/**
+ * tdi_read_struct()
+ */
+etch_structvalue* tdi_read_struct(tagged_data_input* tdi)
+{
+ return NULL;
+}
+
+
+/**
+ * tdi_end_struct()
+ * ends a struct currently being read
+ */
+int tdi_end_struct(tagged_data_input* tdi, etch_structvalue* sv)
+{
+ return 0;
+}
+
+
+/**
+ * tdi_start_array()
+ * starts reading an array from the stream
+ */
+etch_arrayvalue* tdi_start_array(tagged_data_input* tdi)
+{
+ return NULL;
+}
+
+
+etch_arrayvalue* tdi_read_array(tagged_data_input* tdi, etch_validator* v)
+{
+ return NULL;
+}
+
+
+/**
+ * tdi_end_array()
+ */
+int tdi_end_array(tagged_data_input* tdi, etch_arrayvalue* x)
+{
+ return 0;
+}
+
+
+/**
+ * destroy_tagged_data_input()
+ */
+int destroy_tagged_data_input(void* data)
+{
+ tagged_data_input* tdi = (tagged_data_input*)data;
+
+ if (!is_etchobj_static_content(tdi))
+ etch_object_destroy(tdi->impl);
+
+ destroy_objectex((etch_object*)tdi);
+ return 0;
+}
+
+
+/**
+ * clone_tagged_data_input()
+ */
+void* clone_tagged_data_input(void* data)
+{
+ tagged_data_input* tdi = (tagged_data_input*)data;
+ tagged_data_input* newtdi = (tagged_data_input*) clone_object((etch_object*) tdi);
+
+ if (tdi->impl)
+ newtdi->impl = tdi->impl->clone(tdi->impl);
+
+ return newtdi;
+}
+
+
+/**
+ * new_tdi_vtable()
+ */
+i_tagged_data_input* new_tdi_vtable()
+{
+ i_tagged_data_input* vtab = new_vtable(NULL, sizeof(i_tagged_data_input), CLASSID_TDI_VTAB);
+
+ vtab->start_message = tdi_start_message;
+ vtab->read_message = tdi_read_message;
+ vtab->end_message = tdi_end_message;
+ vtab->start_struct = tdi_start_struct;
+ vtab->read_struct = tdi_read_struct;
+ vtab->end_struct = tdi_end_struct;
+ vtab->start_array = tdi_start_array;
+ vtab->read_array = tdi_read_array;
+ vtab->end_array = tdi_end_array;
+ return vtab;
+}
+
+
+/**
+ * new_tagged_data_input()
+ * tagged_data_input constructor
+ */
+tagged_data_input* new_tagged_data_input()
+{
+ i_tagged_data_input* vtab = NULL;
+
+ tagged_data_input* tdi = (tagged_data_input*) new_object
+ (sizeof(tagged_data_input), ETCHTYPEB_TAGDATAINP, CLASSID_TAGDATAINP);
+
+ ((etch_object*)tdi)->destroy = destroy_tagged_data_input;
+ ((etch_object*)tdi)->clone = clone_tagged_data_input;
+
+ vtab = etch_cache_find(get_vtable_cachehkey(CLASSID_TDI_VTAB), 0);
+
+ if(!vtab)
+ {
+ vtab = new_tdi_vtable();
+ etch_cache_insert(((etch_object*)vtab)->get_hashkey(vtab), vtab, FALSE);
+ }
+
+ ((etch_object*)tdi)->vtab = (vtabmask*)vtab;
+ return tdi;
+}
+
+
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_tagdata_out.c b/binding-c/runtime/c/src/main/bindings/msg/etch_tagdata_out.c
new file mode 100644
index 0000000..a167012
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_tagdata_out.c
@@ -0,0 +1,162 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_tagdata_out.c -- tagged_data_output base class default implementation.
+ */
+
+#include "etch_tagdata_out.h"
+#include "etch_cache.h"
+#include "etch_message.h"
+#include "etch_flexbuffer.h"
+#include "etch_arrayval.h"
+#include "etch_objecttypes.h"
+
+/*
+static const char* LOG_CATEGORY = "etch_tdo";
+*/
+int tdo_start_message(tagged_data_output* tdo, etch_message* msg)
+{
+ return -1;
+}
+
+
+int tdo_write_message(tagged_data_output* tdo, etch_message* msg, etch_flexbuffer* fb)
+{
+ return -1;
+}
+
+
+
+int tdo_end_message(tagged_data_output* tdo, etch_message* msg)
+{
+ return NULL;
+}
+
+
+int tdo_start_struct(tagged_data_output* tdo, struct etch_structvalue* sv)
+{
+ return -1;
+}
+
+
+int tdo_write_struct(tagged_data_output* tdo, struct etch_structvalue* sv)
+{
+ return -1;
+}
+
+
+int tdo_end_struct(tagged_data_output* tdo, struct etch_structvalue* sv)
+{
+ return -1;
+}
+
+
+int tdo_start_array(tagged_data_output* tdo, etch_arrayvalue* x)
+{
+ return -1;
+}
+
+
+int tdo_write_array(tagged_data_output* tdo, etch_arrayvalue* x, etch_validator* v)
+{
+ return -1;
+}
+
+
+int tdo_end_array(tagged_data_output* tdo, etch_arrayvalue* x)
+{
+ return -1;
+}
+
+
+/**
+ * destroy_tagged_data_output()
+ */
+int destroy_tagged_data_output(void* data)
+{
+ tagged_data_output* tdo = (tagged_data_output*)data;
+
+ if (!is_etchobj_static_content(tdo))
+ etch_object_destroy(tdo->impl);
+
+ destroy_objectex((etch_object*)tdo);
+ return 0;
+}
+
+
+/**
+ * clone_tagged_data_output()
+ */
+void* clone_tagged_data_output(void* data)
+{
+ tagged_data_output* tdo = (tagged_data_output*)data;
+ tagged_data_output* newtdo = (tagged_data_output*) clone_object((etch_object*) tdo);
+
+ if (tdo->impl)
+ newtdo->impl = tdo->impl->clone(tdo->impl);
+
+ return newtdo;
+}
+
+
+i_tagged_data_output* new_tdo_vtable()
+{
+ i_tagged_data_output* vtab
+ = new_vtable(NULL, sizeof(i_tagged_data_output), CLASSID_TDO_VTAB);
+
+ vtab->start_message = tdo_start_message;
+ vtab->write_message = tdo_write_message;
+ vtab->end_message = tdo_end_message;
+ vtab->start_struct = tdo_start_struct;
+ vtab->write_struct = tdo_write_struct;
+ vtab->end_struct = tdo_end_struct;
+ vtab->start_array = tdo_start_array;
+ vtab->write_array = tdo_write_array;
+ vtab->end_array = tdo_end_array;
+ return vtab;
+}
+
+
+/**
+ * new_tagged_data_output()
+ * tagged_data_output constructor
+ */
+tagged_data_output* new_tagged_data_output()
+{
+ i_tagged_data_output* vtab = NULL;
+
+ tagged_data_output* tdo = (tagged_data_output*) new_object
+ (sizeof(tagged_data_output), ETCHTYPEB_TAGDATAOUT, CLASSID_TAGDATAOUT);
+
+ ((etch_object*)tdo)->destroy = destroy_tagged_data_output;
+ ((etch_object*)tdo)->clone = clone_tagged_data_output;
+
+ vtab = etch_cache_find(get_vtable_cachehkey(CLASSID_TDO_VTAB), 0);
+
+ if(!vtab)
+ {
+ vtab = new_tdo_vtable();
+ etch_cache_insert(((etch_object*)vtab)->get_hashkey(vtab), vtab, FALSE);
+ }
+
+ ((etch_object*)tdo)->vtab = (vtabmask*)vtab;
+ return tdo;
+}
+
+
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_type.c b/binding-c/runtime/c/src/main/bindings/msg/etch_type.c
new file mode 100644
index 0000000..e6c30ee
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_type.c
@@ -0,0 +1,880 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * type.c -- methods on the etch_type object.
+ * type denotes the type of a struct or message. when used with a message
+ * it typically denotes an action or event. an etch_type is a typedef of
+ * etch_id_name, however it has additional instance data specific to type.
+ */
+
+#include "etch_type.h"
+#include "etch_validator.h"
+#include "etch_map.h"
+#include "etch_serializer.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+
+int fieldmap_clear_handler (void* key, void* value);
+etch_hashtable* new_etchtype_fieldmap();
+etch_hashtable* new_etchtype_vtormap();
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * constructors and destructors
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+
+/**
+ * destroy_type()
+ * etch_type destructor
+ */
+int destroy_type(void* data)
+{
+ etch_type* type = (etch_type*)data;
+
+
+ if (!is_etchobj_static_content(type)) /* e.g., not cloned */
+ {
+ if (type->impl)
+ { etch_type_impl* impl = (etch_type_impl*) type->impl;
+ etch_object_destroy(impl);
+ type->impl = NULL;
+ }
+ }
+
+ return destroy_id_name(type);
+}
+
+/**
+ * clone_type()
+ * etch_type quasi copy constructor
+ * originally, type was simply a name and id. now a type has considerable extra
+ * content. we do not clone that content here, but rather copy its reference
+ * from the source and mark the clone as having non-disposable content such that
+ * the type destructor won't try to free it. so the object is not a true clone.
+ * note also that if the original were to be destroyed prior to the clone, the
+ * clone's content memory reference would be hosed; thus we must ensure that we
+ * only clone static types, i.e. types which are instantiated with the service,
+ * (or in the case of unit tests, emulated as such), and destroyed only at
+ * service teardown.
+ *
+ * note finally that etch_type and etch_idname still have the same footprint,
+ * with the type instantiating its extra content at the impl* of the id_name.
+ */
+void* clone_type(void* data)
+{
+ const etch_type* type = (const etch_type*)data;
+ etch_type* newtype = clone_id_name((etch_type*)type);
+ newtype->impl = type->impl;
+ set_etchobj_static_content(newtype);
+ return newtype;
+}
+
+/**
+ * destroy_type_impl()
+ * etch_type_impl destructor
+ */
+int destroy_type_impl(void* data)
+{
+ etch_type_impl* impl = (etch_type_impl*)data;
+
+ if (!is_etchobj_static_content(impl))
+ { /* destruction of the maps causes all fields and non-cached validator
+ * objects, to be destroyed. see etchtype_fieldmap_clear_handler,
+ * and etchtype_vtormap_clear_handler, below. */
+ etch_object_destroy(impl->vtormap);
+ etch_object_destroy(impl->fieldmap);
+ etch_object_destroy(impl->impexphelper);
+ }
+
+ return destroy_objectex((etch_object*)impl);
+}
+
+
+/**
+ * new_type()
+ * etch_type constructor
+ */
+etch_type* new_type(const wchar_t* name)
+{
+ etch_type_impl* impl = NULL;
+ etchparentinfo* inheritlist = NULL;
+ etch_type* newtype = new_id_name(name);
+ if (NULL == newtype) return NULL;
+ ((etch_object*)newtype)->obj_type = ETCHTYPEB_TYPE;
+ ((etch_object*)newtype)->class_id = CLASSID_ID_TYPE;
+
+ /* ensure parent type keys exist in (one-based) inheritance list */
+ inheritlist = get_vtab_inheritance_list((etch_object*)newtype,
+ 2, 1, CLASSID_VTAB_FIELD);
+ inheritlist[1].o.obj_type = ETCHTYPEB_ID_NAME;
+ inheritlist[1].c.class_id = CLASSID_ID_NAME;
+
+ /* instantiate instance data */
+ impl = (etch_type_impl*) new_object(sizeof(etch_type_impl),
+ ETCHTYPEB_IDNAMEIMPL, CLASSID_TYPEIMPL);
+
+ ((etch_object*)impl)->destroy = destroy_type_impl;
+ impl->async_mode = ETCH_ASYNCMODE_NONE; /* where is this default reset? */
+
+ impl->fieldmap = new_etchtype_fieldmap();
+ impl->vtormap = new_etchtype_vtormap();
+
+ newtype->impl = (etch_object*) impl;
+
+ ((etch_object*)newtype)->destroy = destroy_type;
+ ((etch_object*)newtype)->clone = clone_type;
+
+ return newtype;
+}
+
+
+/**
+ * new__static_type()
+ * create a type object whose destructor will have no effect.
+ */
+etch_type* new_static_type(const wchar_t* name)
+{
+ etch_type* newtype = new_type(name);
+ set_etchobj_static_all(newtype);
+ return newtype;
+}
+
+
+
+/**
+ * destroy_static_type()
+ * etch_type destructor.
+ * this should not be set as the virtual dtor for a type, since the type
+ * would then not be quasi-static as desired. it should be invoked explicitly
+ */
+int destroy_static_type(etch_type* type)
+{
+ clear_etchobj_static_all(type);
+ return destroy_type(type);
+}
+
+
+
+
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * get/set
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/* mutators are implemented only for instance data specific to type, i.e.
+ * resident in the etch_type_impl, following the c binding convention that we
+ * implement a mutator only when the datum can't safely be get/set directly.
+ */
+
+/**
+ * etchtype_set_type_stubhelper()
+ * set type's 'stub helper', returning existing helper if any.
+ * the stub helper is a function pointer, not an object.
+ */
+opaque_stubhelper etchtype_set_type_stubhelper(etch_type* type, opaque_stubhelper helper)
+{
+ /* sets a callback from message type to the particular API method implementation.
+ * note that in the the java binding this is indirect, that is, this callback
+ * calls a wrapper method which calls the implementation. however in our case,
+ * the methods have same signature, so we call the implementation directly.
+ * keep an eye on this though in case I have missed something.
+ */
+ opaque_stubhelper oldhelper = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl)
+ { oldhelper = impl->stubhelper;
+ impl->stubhelper = helper;
+ }
+ return oldhelper;
+}
+
+
+/**
+ * etchtype_get_type_stubhelper()
+ * getter for stub helper - see comments at set_type_stubhelper()
+ */
+opaque_stubhelper etchtype_get_type_stubhelper(etch_type* type)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ return impl? impl->stubhelper: NULL;
+}
+
+
+/**
+ * etchtype_set_result_type()
+ * set result type, returning existing result type if any.
+ * @param type a non-disposable reference to a type.
+ * todo: if result type is set only at construction, lose this.
+ */
+etch_type* etchtype_set_result_type(etch_type* type, etch_type* rtype)
+{
+ etch_type* oldtype = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl)
+ { oldtype = impl->result_type;
+ impl->result_type = rtype;
+ }
+ return oldtype;
+}
+
+
+/**
+ * etchtype_get_result_type()
+ * returns a non-disposable reference to result type
+ */
+etch_type* etchtype_get_result_type(etch_type* type)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ return impl? impl->result_type: NULL;
+}
+
+
+/**
+ * etchtype_set_super_type()
+ * todo: if set only at construction, lose this.
+ * @param a non-disposable type
+ * @return the previous type, not disposable.
+ */
+etch_type* etchtype_set_super_type(etch_type* type, etch_type* stype)
+{
+ etch_type* oldtype = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl)
+ { oldtype = impl->super_type;
+ impl->super_type = stype;
+ }
+ return oldtype;
+}
+
+
+/**
+ * etchtype_get_super_type()
+ * returns a non-disposable reference to super type
+ */
+etch_type* etchtype_get_super_type(etch_type* type)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ return impl? impl->super_type: NULL;
+}
+
+
+/**
+ * etchtype_set_component_type()
+ * set associated component type and class for an array of this class.
+ * @param type a non-disposable type object. neither owned nor stored here.
+ * @return the existing objtype/classid representing component type.
+ */
+unsigned int etchtype_set_component_type(etch_type* type, unsigned int typeclass)
+{
+ unsigned int old_class = 0;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl)
+ { old_class = impl->component_class;
+ impl->component_class = typeclass;
+ }
+ return old_class;
+}
+
+
+/**
+ * etchtype_get_component_type()
+ * returns component obj_type and class_id or zero indicating none set.
+ */
+unsigned int etchtype_get_component_type(etch_type* type)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ return impl? impl->component_class: 0;
+}
+
+
+/**
+ * etchtype_set_async_mode()
+ * set async_mode, which determines if requests are run on the queued or
+ * free thread pool.
+ */
+unsigned char etchtype_set_async_mode (etch_type* type, unsigned char mode)
+{
+ unsigned char old_mode = 0;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl)
+ { old_mode = impl->async_mode;
+ impl->async_mode = mode;
+ }
+ return old_mode;
+}
+
+
+/**
+ * etchtype_get_async_mode()
+ * returns component async_mode
+ */
+unsigned char etchtype_get_async_mode (etch_type* type)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ return impl? impl->async_mode: 0;
+}
+
+
+/**
+ * etchtype_set_timeout()
+ * set timeout to wait for response, in milliseconds.
+ */
+unsigned int etchtype_set_timeout(etch_type* type, unsigned int ms)
+{
+ unsigned int old_timeout = 0;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl)
+ { old_timeout = impl->timeout;
+ impl->timeout = ms;
+ }
+ return old_timeout;
+}
+
+
+/**
+ * etchtype_get_timeout()
+ * get timeout to wait for response, in milliseconds.
+ */
+unsigned int etchtype_get_timeout(etch_type* type)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ return impl? impl->timeout: 0;
+}
+
+
+/**
+ * etchtype_set_run_validators()
+ * set boolean run validators flag, returning existing value.
+ */
+unsigned char etchtype_set_run_validators(etch_type* type, unsigned char val)
+{
+ unsigned char old_flag = 0;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl)
+ { old_flag = impl->is_run_validators;
+ impl->is_run_validators = val;
+ }
+ return old_flag;
+}
+
+
+/**
+ * etchtype_get_response_field()
+ */
+etch_field* etchtype_get_response_field(etch_type* type)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ return impl? impl->response_field: NULL;
+}
+
+
+/**
+ * etchtype_set_response_field()
+ */
+etch_field* etchtype_set_response_field(etch_type* type, etch_field* field)
+{
+ etch_field* old_field = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl)
+ { old_field = impl->response_field;
+ impl->response_field = field;
+ }
+ return old_field;
+}
+
+
+/**
+ * etchtype_get_run_validators()
+ * get boolean run validators flag.
+ */
+unsigned char etchtype_get_run_validators(etch_type* type)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ return impl? impl->is_run_validators: 0;
+}
+
+
+/**
+ * etchtype_set_impexphelper()
+ * setter for import/export helper object
+ * @param helper the helper object to be assigned, or null.
+ * caller relinquishes ownership of this object if present.
+ * @return the *disposable* prior helper object if any.
+ * if present, caller now owns this object.
+ */
+etch_serializer* etchtype_set_impexphelper(etch_type* type, etch_serializer* helper)
+{
+ etch_serializer* oldhelper = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl)
+ { oldhelper = impl->impexphelper;
+ impl->impexphelper = helper;
+ }
+ return oldhelper;
+}
+
+
+/**
+ * etchtype_get_impexphelper()
+ * getter for import/export helper object
+ * @return a *non-disposable* reference to helper object.
+ * the type retains ownership of this object.
+ */
+etch_serializer* etchtype_get_impexphelper(etch_type* type)
+{
+ etch_type_impl* impl = type? (etch_type_impl*) type->impl: NULL;
+ return impl? impl->impexphelper: NULL;
+}
+
+
+/**
+ * etchtype_is_assignable_from()
+ * indicate if this type is assignable from other, i.e. other a subclass of this
+ */
+int etchtype_is_assignable_from(etch_type* type, etch_type* othertype)
+{
+ etch_type* other_supertype;
+ if (NULL == othertype) return FALSE;
+ if (is_equal_types(type, othertype)) return TRUE;
+ other_supertype = othertype->impl?
+ ((etch_type_impl*)othertype->impl)->super_type: NULL;
+ return etchtype_is_assignable_from(type, other_supertype);
+}
+
+
+/* - - - - - - - - - - - - - - - - -
+ * fieldmap accessors
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * get_key_by_name()
+ * look up an etch_id_name key object by a name string. recall that etch_type
+ * and etch_field each are typedefs of etch_id_name. id_name derivations are
+ * keyed by hash of name, so lookup is direct;
+ */
+etch_id_name* etchtype_get_key_by_name(etch_hashtable* map, const wchar_t* name)
+{
+ etch_hashitem hashbucket, *thisitem = &hashbucket;
+ const unsigned hashkey = etch_get_wchar_hashkey(name);
+ const int result = ((struct i_hashtable*)((etch_object*)map)->vtab)->findh(map->realtable, hashkey, map, (void**)&thisitem);
+ return result == 0? (etch_id_name*) thisitem->key: NULL;
+}
+
+
+/**
+ * get_idname_by_id()
+ * given a hashtable and an id_name "id", return the map's id_name key having that id.
+ * note that a non-disposable *reference* is returned, not a copy.
+ */
+etch_id_name* etchtype_get_key_by_id (etch_hashtable* map, const unsigned id)
+{
+ etch_iterator iterator;
+
+ hashtable_getlock(map);
+ set_iterator(&iterator, map, &map->iterable);
+ while(iterator.has_next(&iterator))
+ {
+ etch_id_name* this_idname = (etch_id_name*) iterator.current_key;
+ if (this_idname->id == id) {
+ hashtable_rellock(map);
+ return this_idname;
+ }
+ iterator.next(&iterator);
+ }
+ hashtable_rellock(map);
+ return NULL;
+}
+
+
+/**
+ * etchtype_add_field()
+ * adds a field to set of fields
+ * @param field caller must supply a disposable field object.
+ * @return the argument. If there is a name collision, the existing field
+ * is returned in place of the supplied field, AND the supplied field is
+ * DESTROYED. this simplifies logic up the line, and is consistent with caller
+ * expecting to relinquish responsibility for the field passed.
+ */
+etch_field* etchtype_add_field (etch_type* type, etch_field* field)
+{
+ etch_field *effective_field = field;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ etch_hashtable* map = impl->fieldmap;
+
+ int result = ((struct i_hashtable*)((etch_object*)impl->fieldmap)->vtab)->inserth
+ (map->realtable, field, NULL, map, 0);
+
+ if (-1 == result)
+ effective_field = etchtype_get_field_by_name(type, field->name);
+
+ if (effective_field != field)
+ etch_object_destroy(field);
+
+ #ifdef ETCHTYPE_DEBUG
+ if (effective_field)
+ wprintf(L"add field %08x '%s'\n",
+ (size_t) (void*) effective_field, effective_field->name);
+ else wprintf(L"error adding field '%s'\n", field->name);
+ #endif
+
+ return effective_field;
+}
+
+
+/**
+ * etchtype_get_field_by_id()
+ * @return a non-disposable reference to the requested field, or null
+ */
+etch_field* etchtype_get_field_by_id (etch_type* type, const unsigned id)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ return etchtype_get_key_by_id(impl->fieldmap, id);
+}
+
+
+/**
+ * etchtype_get_field_by_name()
+ * works as in the java binding, in that if the type does not include
+ * a field with that name, a new field is created and added to the type.
+ * @return a non-disposable reference to the requested field, or null
+ */
+etch_field* etchtype_get_field_by_name (etch_type* type, const wchar_t* name)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ etch_field* field = etchtype_get_key_by_name(impl->fieldmap, name);
+ if (NULL == field)
+ field = etchtype_add_field (type, new_field(name));
+ return field;
+}
+
+
+/**
+ * etchtype_get_fields()
+ * returns a disposable arraylist of references. the list is marked
+ * such that list->destroy() will not attempt to free content.
+ * caller must cast result to etch_arraylist*
+ */
+void* etchtype_get_fields (etch_type* type)
+{
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ return get_map_keys(impl->fieldmap);
+}
+
+
+/*
+ * etchtype_fields_count()
+ * return count of fields resident in the type.
+ */
+int etchtype_fields_count(etch_type* type)
+{
+ int count = 0;
+ etch_hashtable* map = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl) map = impl->fieldmap;
+ if (map) count = ((struct i_hashtable*)((etch_object*)map)->vtab)->count(map->realtable, 0, 0);
+ return count;
+}
+
+
+/*
+ * etchtype_set_fields_iterator()
+ * initialize iterator over fields.
+ */
+int etchtype_set_fields_iterator(etch_type* type, etch_iterator* iterator)
+{
+ etch_hashtable* map = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl) map = impl->fieldmap;
+ return map? set_iterator(iterator, map, &map->iterable): -1;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - -
+ * validator insert/lookup
+ * - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchtype_get_validator_by_id()
+ * caller will want to cast result to etch_validator*.
+ * note that the etch_type header can't include etch_validator header,
+ * thus the anonymous pointers to etch_validator in these methods.
+ */
+etch_object* etchtype_get_validator_by_id (etch_type* type, const unsigned id)
+{
+ etch_hashtable* map;
+ etch_iterator iterator;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (!impl || !id) return NULL;
+ map = impl->vtormap;
+
+ set_iterator(&iterator, map, &map->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ etch_field* this_field = (etch_field*) iterator.current_key;
+ if (this_field->id == id)
+ return iterator.current_value;
+ iterator.next(&iterator);
+ }
+
+ return NULL;
+}
+
+
+/**
+ * etchtype_get_validator_by_name()
+ * @param name the name of the etch_field keying the validator.
+ * caller will want to cast result to etch_validator*
+ */
+etch_object* etchtype_get_validator_by_name (etch_type* type, const wchar_t* name)
+{
+ etch_type_impl* impl = type? (etch_type_impl*) type->impl: NULL;
+
+ if (impl && name)
+ {
+ etch_hashitem hashbucket, *thisitem = &hashbucket;
+ etch_hashtable* map = impl->vtormap;
+ const unsigned key = etch_get_wchar_hashkey(name);
+ if (0 == ((struct i_hashtable*)((etch_object*)map)->vtab)->findh(map->realtable, key, map, (void**)&thisitem))
+ return thisitem->value;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * etchtype_put_validator()
+ * adds a validator to validator chain for specified key
+ * @param field relinquished regardless of result.
+ * @param new_vtor relinquished regardless of result.
+ */
+int etchtype_put_validator (etch_type* type, etch_field* field, etch_object* new_vtor)
+{
+ int result = -1;
+ etch_hashtable *fmap, *vmap;
+ etch_hashitem hashbucket, *mapentry = &hashbucket;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ ETCH_ASSERT(impl);
+
+ do /* validate parameters */
+ {
+ if (!is_etch_validator(new_vtor)) {
+ etch_object_destroy(field);
+ field = NULL;
+ break;
+ }
+ if (NULL == field) {
+ etch_object_destroy(new_vtor);
+ new_vtor = NULL;
+ break;
+ }
+ result = 0;
+ } while(0);
+
+ if (0 != result) return result;
+
+ if (!impl || !is_etch_validator(new_vtor) || !field) return -1;
+ memset(mapentry, 0, sizeof(etch_hashitem));
+ fmap = impl->fieldmap;
+ vmap = impl->vtormap;
+
+ /* add field to fieldmap. if an eponymous field was present, it is returned
+ * in place of caller's field, and caller's field has been destroyed. */
+ field = etchtype_add_field (type, field);
+
+ /* if a validator exists under the specified key, we'll chain the new
+ * validator to the existing validator with a new combo validator object,
+ * and replace the current vtor map entry with the new combo validator.
+ */
+ result = ((struct i_hashtable*)((etch_object*)vmap)->vtab)->removeh /* if already in vtormap, remove it */
+ (vmap->realtable, ((etch_object*)field)->get_hashkey(field), vmap, (void**)&mapentry);
+
+ if (result == -1) /* if it was not already in vtormap, insert it */
+ result = ((struct i_hashtable*)((etch_object*)vmap)->vtab)->inserth (vmap->realtable,
+ etch_object_clone_func(field), new_vtor, vmap, 0);
+ else /* ... otherwise insert a new chain head */
+ { etch_field* existing_key = (etch_field*) mapentry->key;
+ etch_validator* existing_vtor = (etch_validator*) mapentry->value;
+
+ etch_validator* vcombo /* chain new vtor to existing vtor ... */
+ = new_combo_validator(existing_vtor, (etch_validator*) new_vtor);
+
+ result = ((struct i_hashtable*)((etch_object*)vmap)->vtab)->inserth /* ... and insert the new chained vtor */
+ (vmap->realtable, existing_key, vcombo, vmap, 0);
+ }
+
+ return result;
+}
+
+
+/*
+ * etchtype_clear_validator()
+ * remove the validator chain for specified key.
+ */
+int etchtype_clear_validator(etch_type* type, etch_field* key)
+{
+ int result = -1;
+ etch_hashtable* map = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl) map = impl->vtormap;
+
+ if (map)
+ { etch_validator* removed_vtor = NULL;
+ etch_hashitem hashbucket, *mapentry = &hashbucket;
+ memset(mapentry, 0, sizeof(etch_hashitem));
+
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->removeh(map->realtable, ((etch_object*)key)->get_hashkey(key), map, (void**)&mapentry);
+
+ if (result == 0) /* returned content is head of validator chain */ {
+ removed_vtor = (etch_validator*) mapentry->value;
+ destroy_type((etch_type*) mapentry->key);
+ }
+
+ if (removed_vtor) /* destructors are called up the chain */
+ etch_object_destroy(removed_vtor);
+ }
+
+ return 0;
+}
+
+
+/*
+ * etchtype_clear_validators()
+ * clear all type validators. non-cached validators are destroyed.
+ * returns count of items cleared, or -1 if error.
+ */
+int etchtype_clear_validators(etch_type* type)
+{
+ int result = -1;
+ etch_hashtable* map = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl) map = impl->vtormap;
+ if (map) /* we ask hashtable clear() to call content destructors */
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->clear(map->realtable, FALSE, TRUE, map, 0);
+ return result;
+}
+
+
+/*
+ * etchtype_validators_count()
+ * return count of validators in chain.
+ */
+int etchtype_validators_count(etch_type* type)
+{
+ int count = 0;
+ etch_hashtable* map = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl) map = impl->vtormap;
+ if (map) count = ((struct i_hashtable*)((etch_object*)map)->vtab)->count(map->realtable, 0, 0);
+ return count;
+}
+
+
+/*
+ * etchtype_set_validators_iterator()
+ * initialize iterator over validators.
+ */
+int etchtype_set_validators_iterator(etch_type* type, etch_iterator* iterator)
+{
+ etch_hashtable* map = NULL;
+ etch_type_impl* impl = (etch_type_impl*) type->impl;
+ if (impl) map = impl->vtormap;
+ return map? set_iterator(iterator, map, &map->iterable): -1;
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * utility methods
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * etchtype_fieldmap_clear_handler()
+ * callback set to handle freeing of key memory during a clear() of the fields
+ * map. note that this map is used as a quasi set, so the value is always null.
+ * the etch_fields are owned by the map and destroyed as the map is destroyed.
+ * handlers return FALSE to indicate memory free NOT handled.
+ */
+int etchtype_fieldmap_clear_handler (void* key, void* value)
+{
+ ((etch_object*)key)->destroy(key);
+ return TRUE;
+}
+
+
+/**
+ * new_etchtype_fieldmap()
+ * construct and return a hashtable configured as expected for the fieldmap
+ */
+etch_hashtable* new_etchtype_fieldmap()
+{
+ etch_hashtable* map = new_hashtable_synchronized(ETCHTYPE_DEFSIZE_FIELDMAP);
+ if (NULL == map) return NULL;
+ map->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+ map->is_tracked_memory = TRUE;
+ map->is_readonly_keys = FALSE; /* keys are disposable field objects */
+ map->is_readonly_values = FALSE; /* value is always null */
+ map->freehook = etchtype_fieldmap_clear_handler;
+ return map;
+}
+
+
+/*
+ * etchtype_vtormap_clear_handler()
+ * callback set to handle freeing of field and validator memory
+ * during a clear() of the validators map.
+ */
+int etchtype_vtormap_clear_handler (void* key, void* value)
+{
+ /* note that value->destroy() is invoking the validator destructor,
+ * and that this will have no effect on a validator marked as cached */
+ etch_object_destroy(value);
+ etch_object_destroy(key);
+ return TRUE;
+}
+
+
+/**
+ * new_etchtype_vtormap()
+ * construct and return a hashtable configured as expected for validators.
+ * this is a map of non-disposable etch_field keys to etch_validator*
+ * object references. the validators are considered as disposable, in that
+ * clearing the map will result in validator destructor calls; however
+ * note that destroy() has no effect on a cached validator - these are
+ * not destroyed until such time as the validator cache is cleared.
+ */
+etch_hashtable* new_etchtype_vtormap()
+{
+ etch_hashtable* map = new_hashtable_synchronized(ETCHTYPE_DEFSIZE_VTORMAP);
+ if (NULL == map) return NULL;
+ map->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+ map->is_tracked_memory = TRUE;
+ map->is_readonly_keys = TRUE; /* keys are nondisposable field objects */
+ map->is_readonly_values = FALSE; /* values are validator function pointers */
+ map->freehook = etchtype_vtormap_clear_handler;
+ return map;
+}
+
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_validator.c b/binding-c/runtime/c/src/main/bindings/msg/etch_validator.c
new file mode 100644
index 0000000..1771251
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_validator.c
@@ -0,0 +1,1767 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_validator.c
+ * validators
+ */
+
+#include "etch_validator.h"
+#include "etch_tagged_data.h"
+#include "etch_cache.h"
+#include "etch_arrayval.h"
+#include "etch_structval.h"
+#include "etch_encoding.h"
+#include "etch_nativearray.h"
+#include "etch_objecttypes.h"
+#include "etch_type.h"
+#include "etch_mem.h"
+
+#ifdef WIN32
+#pragma warning (disable:4996)
+#endif
+
+etch_validator* etchvtor_cache[ETCHVTOR_CACHED_TYPE_COUNT * ETCHVTOR_MAX_CACHED];
+
+etch_validator** etchvtor_cache_boolean; /* boolean validator cache address */
+etch_validator** etchvtor_cache_byte; /* byte validator cache address */
+etch_validator** etchvtor_cache_int8; /* int8 validator cache address */
+etch_validator** etchvtor_cache_int16; /* int16 validator cache address */
+etch_validator** etchvtor_cache_int32; /* int32 validator cache address */
+etch_validator** etchvtor_cache_int64; /* int64 validator cache address */
+etch_validator** etchvtor_cache_float; /* float validator cache address */
+etch_validator** etchvtor_cache_double; /* double validator cache address */
+etch_validator** etchvtor_cache_string; /* string validator cache address */
+etch_validator** etchvtor_cache_object; /* object validator cache address */
+etch_validator** etchvtor_cache_exception; /* excp validator cache address */
+etch_validator** etchvtor_cache_eod; /* eod validator cache address */
+
+
+/* cached validators have private constructors,
+ * get() is the public method of construction and caching
+ */
+etch_validator* new_validator_boolean(const int dimensions);
+etch_validator* new_validator_byte (const int dimensions);
+etch_validator* new_validator_int8 (const int dimensions);
+etch_validator* new_validator_int16 (const int dimensions);
+etch_validator* new_validator_int32 (const int dimensions);
+etch_validator* new_validator_int64 (const int dimensions);
+etch_validator* new_validator_float (const int dimensions);
+etch_validator* new_validator_double (const int dimensions);
+etch_validator* new_validator_string (const int dimensions);
+etch_validator* new_validator_object (const int dimensions);
+etch_validator* new_validator_exception();
+etch_validator* new_validator_eod();
+
+int etch_typevtor_validate(etch_validator*, etch_object*);
+int etch_typevtor_check_value(etch_validator*, etch_object*, byte*);
+etch_validator* etch_typevtor_element_validator(etch_validator*);
+
+etch_object* etch_typevtor_validate_value(etch_validator*, etch_object*);
+etch_validator* etchvtor_cache_validator(etch_validator*, etch_validator** cache);
+etch_validator* new_type_validator(const unsigned short vtor_classid,
+ const unsigned short scalar_obj_type, const unsigned short scalar_classid,
+ const unsigned short array_classid, const int ndims, char* description);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * contructors, destructors
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * destroy_validator()
+ * validator object destructor
+ */
+int destroy_validator(void* data)
+{
+ etch_validator* vtor = (etch_validator*)data;
+ /* don't honor destroy() request if object is cached */
+ if (vtor->is_cached) return -1;
+
+ if (!is_etchobj_static_content(vtor))
+ etch_free(vtor->description);
+
+ return destroy_objectex((etch_object*)vtor);
+}
+
+
+/**
+ * new_validator_from()
+ * constructor for etch_validator
+ */
+etch_validator* new_validator()
+{
+ return new_validator_from(NULL, NULL, NULL, NULL);
+}
+
+
+/**
+ * new_validator_from()
+ * constructor 2 for etch_validator
+ */
+etch_validator* new_validator_from(etchvtor_validate fv, etchvtor_checkvalue fcv,
+ etchvtor_element_validator fev, etchvtor_validate_value fvv)
+{
+ etch_validator* newvtor = (etch_validator*) new_object(sizeof(etch_validator),
+ ETCHTYPEB_VALIDATOR, CLASSID_VALIDATOR);
+
+ ((etch_object*)newvtor)->destroy = destroy_validator;
+ newvtor->validate = fv;
+ newvtor->check_value = fcv;
+ newvtor->element_validator = fev;
+ newvtor->validate_value = fvv;
+
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * validator cache
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchvtor_cache_validator()
+ * convenience method to cache specified validator, marking it cached.
+ */
+etch_validator* etchvtor_cache_validator(etch_validator* vtor, etch_validator** cache)
+{
+ cache[vtor->numdimensions] = vtor; /* assumed pre-validated by caller */
+ vtor->is_cached = TRUE;
+ return vtor;
+}
+
+
+/**
+ * etchvtor_clear_cache()
+ * clear the validator cache, destroying any validators found
+ */
+void etchvtor_clear_cache()
+{
+ const int cachebytes = ETCHVTOR_BYTES_PER_CACHE * ETCHVTOR_CACHED_TYPE_COUNT;
+ const int cacheslots = cachebytes / sizeof(void*);
+ int i=0;
+
+ for(; i < cacheslots; i++)
+ {
+ etch_validator* p = etchvtor_cache[i];
+ if (p == NULL || ((etch_object*)p)->obj_type != ETCHTYPEB_VALIDATOR) continue;
+ p->is_cached = FALSE;
+ etch_object_destroy(p);
+ }
+
+ memset(etchvtor_cache, 0, cachebytes);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * combo validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+
+/**
+ * etch_combovtor_validate()
+ * combo validator default validate() virtual
+ */
+int etch_combovtor_validate(etch_validator* vtor, etch_object* value)
+{
+ int resulta = 0, resultb = 0;
+ etch_validator *vtor_a = vtor->vtor_a, *vtor_b = vtor->vtor_b;
+
+ resulta = vtor_a? vtor_a->validate(vtor_a, value): -1;
+ if (0 == resulta) return 0;
+
+ resultb = vtor_b? vtor_b->validate(vtor_b, value): -1;
+ return resultb;
+}
+
+
+/**
+ * etch_combovtor_validate_value()
+ * combo validator default validate_value() virtual
+ * note carefully: the returned value may or may not be the same object
+ * as the passed value. see comments later in this module.
+ */
+etch_object* etch_combovtor_validate_value(etch_validator* vtor, etch_object* value)
+{
+ etch_validator *vtor_a = vtor->vtor_a, *vtor_b = vtor->vtor_b;
+ etch_object *validatedobj = NULL;
+
+ validatedobj = vtor_a? vtor_a->validate_value(vtor_a, value): NULL;
+
+ if (NULL == validatedobj)
+ {
+ validatedobj = vtor_b? vtor_b->validate_value(vtor_b, value): NULL;
+ }
+
+ return validatedobj;
+}
+
+
+/**
+ * etch_combovtor_check_value()
+ * combo validator default check_value() virtual
+ */
+int etch_combovtor_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ byte typecode = 0;
+ int resulta = 0, resultb = 0;
+ etch_validator *vtor_a = vtor->vtor_a, *vtor_b = vtor->vtor_b;
+
+ resulta = vtor_a? vtor_a->check_value(vtor_a, value, &typecode): -1;
+ if (-1 == resulta)
+ { resultb = vtor_b? vtor_b->check_value(vtor_b, value, &typecode): -1;
+ if (-1 == resultb) return -1;
+ }
+
+ *typecode_out = typecode;
+ return 0;
+}
+
+
+/**
+ * etch_combovtor_element_validator()
+ * combo validator default element_validator() virtual
+ */
+etch_validator* etch_combovtor_element_validator(etch_validator* vtor)
+{
+ etch_validator *vtor_a = vtor->vtor_a, *vtor_b = vtor->vtor_b;
+
+ etch_validator *eltvtor_a = vtor_a? vtor_a->element_validator(vtor_a): NULL;
+ etch_validator *eltvtor_b = vtor_b? vtor_b->element_validator(vtor_b): NULL;
+
+ if (eltvtor_a == NULL && eltvtor_b == NULL) return NULL;
+ if (eltvtor_a == NULL) return eltvtor_b;
+ if (eltvtor_b == NULL) return eltvtor_a;
+ return new_combo_validator(eltvtor_a, eltvtor_b);
+}
+
+
+/**
+ * destroy_combo_validator()
+ * combo validator destructor
+ * destroys all validators in the chain
+ */
+int destroy_combo_validator(void* data)
+{
+ etch_validator* vtor = (etch_validator*)data;
+ if (vtor->is_cached) return -1;
+
+ if (!is_etchobj_static_content(vtor))
+ {
+ etch_object_destroy(vtor->vtor_a);
+ etch_object_destroy(vtor->vtor_b);
+ etch_free(vtor->description);
+ }
+
+ return destroy_objectex((etch_object*)vtor);
+}
+
+
+/**
+ * new_combo_validator()
+ * constructor for combo validator.
+ * caveat: the destructor frees memory for all validators in the chain.
+ * if the same validator reference were to appear more than once in a chain,
+ * it must be either a reference from the cache, or otherwise marked such that
+ * the destructor will not attempt to free it (validator marked as static,
+ * or combo parent marked as static content). the safest way to chain non-
+ * cached validators is to ensure that each chained validator reference is
+ * unique, i.e. newly instantiated.
+ */
+etch_validator* new_combo_validator(etch_validator* vtor_a, etch_validator* vtor_b)
+{
+ etch_validator* newvtor = new_validator_from
+ (etch_combovtor_validate,
+ etch_combovtor_check_value,
+ etch_combovtor_element_validator,
+ etch_combovtor_validate_value);
+
+ ((etch_object*)newvtor)->class_id = CLASSID_COMBO_VALIDATOR;
+ ((etch_object*)newvtor)->destroy = destroy_combo_validator;
+ newvtor->vtor_a = vtor_a;
+ newvtor->vtor_b = vtor_b;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * type validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_type_validator()
+ * private constructor for a type validator
+ */
+etch_validator* new_type_validator(const unsigned short vtor_classid,
+ const unsigned short scalar_obj_type, const unsigned short scalar_classid,
+ const unsigned short array_classid, const int ndims, char* description)
+{
+ etch_validator* newvtor = new_validator();
+ ((etch_object*)newvtor)->class_id = vtor_classid;
+ newvtor->expected_class_id = ndims? array_classid: scalar_classid;
+ newvtor->numdimensions = ndims;
+ newvtor->description = new_char(description);
+ newvtor->validate_value = etch_typevtor_validate_value;
+ newvtor->validate = etch_typevtor_validate;
+ newvtor->check_value = etch_typevtor_check_value;
+ newvtor->element_validator = etch_typevtor_element_validator;
+ return newvtor;
+}
+
+
+/**
+ * new_type_validator_1()
+ * constructor for a type validator for types using inheritance scheme 1
+ * @param vtable_class_id the class of the expected class' vtable, zero if the
+ * class inherits only from object. the class inheritance list if any is fetched
+ * from the cached vtable, and is not disposable.
+ */
+etch_validator* new_type_validator_1(const unsigned short vtor_classid,
+ const unsigned short scalar_obj_type, const unsigned short scalar_classid,
+ const unsigned short vtable_class_id, const unsigned short array_classid,
+ const int ndims, char* description)
+{
+ etch_validator* newvtor = new_type_validator(vtor_classid, scalar_obj_type,
+ scalar_classid, array_classid, ndims, description);
+
+ if (vtable_class_id)
+ { /* cache a reference to the expected class' inheritance list if any */
+ vtabmask* vtab = etch_cache_find(get_vtable_cachehkey(vtable_class_id), 0);
+ if (vtab)
+ { newvtor->inherits_from = vtab->inherits_from;
+ newvtor->is_owned_inherits_from = FALSE;
+ }
+ }
+
+ return newvtor;
+}
+
+
+/**
+ * new_type_validator_2()
+ * constructor for a type validator
+ * @param inherit_list a complete and disposable inheritance list for the class.
+ * for classes inheriting via method 2 that have more than two objects in the
+ * inheritance chain, this list must be artificially created, since no object
+ * in the chain will have a complete list.
+ */
+etch_validator* new_type_validator_2(const unsigned short vtor_classid,
+ const unsigned short scalar_obj_type, const unsigned short scalar_classid,
+ const unsigned short array_classid, etchparentinfo* inherit_list,
+ const int ndims, char* description)
+{
+ etch_validator* newvtor = new_type_validator(vtor_classid, scalar_obj_type,
+ scalar_classid, array_classid, ndims, description);
+
+ newvtor->inherits_from = inherit_list;
+ newvtor->is_owned_inherits_from = TRUE;
+ return newvtor;
+}
+
+
+/**
+ * etch_typevtor_check_dimensions()
+ * convenience function to validate dimension count
+ */
+int etchvtor_check_dimensions(const int ndims)
+{
+ return ndims < 0 || ndims > ETCHVTOR_MAX_NDIMS? -1: 0;
+}
+
+
+/**
+ * etchvtor_set_classparams()
+ * populate arguments to etchobj_is_assignable_from(), for use by validate()
+ */
+void etchvtor_set_classparams(etch_objclass* targetparams,
+ etch_objclass* sourceparams, etch_validator* targetvtor, etch_object* sourceobj)
+{
+ memset(targetparams, 0, sizeof(etch_objclass));
+ ((etch_objclass*)targetparams)->obj_type = targetvtor->expected_obj_type;
+ ((etch_objclass*)targetparams)->class_id = targetvtor->expected_class_id;
+ targetparams->numdims = targetvtor->numdimensions;
+ targetparams->inherits_from = targetvtor->inherits_from;
+
+ set_etch_assignable_arg_from(sourceparams, sourceobj);
+}
+
+
+/**
+ * etch_typevtor_validate()
+ * type validator default validate() virtual
+ */
+int etch_typevtor_validate(etch_validator* vtor, etch_object* value)
+{
+ int result = 0, source_numdims = 0;
+ if (!value) return -1;
+
+ /* java uses different classes for arrays of the same type but different
+ * dimensions. we do not, so we also validate dimensions of array objects
+ * to augment class comparison.
+ */
+ if (is_etch_object_type(((etch_object*)value)->obj_type, ((etch_object*)value)->class_id)) return 0;
+
+ switch(((etch_object*)value)->obj_type)
+ { case ETCHTYPEB_NATIVEARRAY:
+ source_numdims = ((etch_nativearray*)value)->numdims;
+ break;
+ case ETCHTYPEB_ARRAYVAL:
+ source_numdims = ((etch_arrayvalue*)value)->dim;
+ break;
+ case ETCHTYPEB_ARRAYLIST:
+ source_numdims = 1;
+ break;
+ }
+
+ #if(0)
+ /* this validation was moved to the object validator's validate() */
+ if (is_etch_object_type(((etch_object*)value)->obj_type, ((etch_object*)value)->class_id) && vtor->numdims == 0)
+ result = 0;
+ else
+ #endif
+ if (source_numdims != vtor->numdimensions)
+ result = -1;
+ else
+ if (((etch_object*)value)->class_id != vtor->expected_class_id)
+ {
+ /* objects not same class: verify source class assignable to target */
+ etch_objclass target, source;
+ etchvtor_set_classparams(&target, &source, vtor, (etch_object*) value);
+
+ if (etchobj_is_assignable_from(&target, &source));
+ else result = -1;
+ }
+
+ return result;
+}
+
+
+/**
+ * etch_typevtor_check_value()
+ * default implementation
+ */
+int etch_typevtor_check_value(etch_validator* v, etch_object* x, byte* tc)
+{
+ return -1;
+}
+
+
+/**
+ * etch_typevtor_element_validator()
+ * default implementation
+ */
+etch_validator* etch_typevtor_element_validator(etch_validator* v)
+{
+ return NULL;
+}
+
+
+/**
+ * etch_typevtor_validate_value()
+ * type validator default validate_value() virtual.
+ * note carefully: the returned value may or may not be the same object as the
+ * passed value. the reason for this clunkiness is the port from java, in which
+ * the original could simply be abandoned. the caller must therefore test if the
+ * objects are the same, and if not, destroy the original. for this reason also,
+ * this method should not be called with the same symbol specified for both the
+ * passed and return objects, for this would leak memory.
+ * note further: the further reason for this is, for example, that the caller
+ * may get back an etch_byte*, but the object being validated is an etch_int32*
+ * whose value is downsized into a newly instantiated etch_byte. we have padded
+ * out all the primitives to have 8 bytes after the header, so we could, if need
+ * be, morph the object passed to validate_value to be the validated object type.
+ * for example, if we passed an etch_int32 with value 1 to the byte validator,
+ * currently byte's validate_value would instantiate and return a new etch_byte,
+ * but we could easily change the etch_int32 to an etch_byte, if this would help.
+ */
+etch_object* etch_typevtor_validate_value(etch_validator* vtor, etch_object* value)
+{
+ return (0 == vtor->validate(vtor, value))? value: NULL;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * boolean validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_boolean /* boolean validator cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_BOOLEAN * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_boolean_get()
+ * get a boolean validator for specified dimensions
+ */
+etch_validator* etchvtor_boolean_get(const int ndims)
+{
+ etch_validator* vtor = NULL;
+ if (-1 == etchvtor_check_dimensions(ndims)) return NULL;
+
+ if (ndims >= ETCHVTOR_MAX_CACHED)
+ vtor = new_validator_boolean(ndims);
+ else
+ if (NULL == (vtor = etchvtor_cache_boolean[ndims]))
+ vtor = etchvtor_cache_validator
+ (new_validator_boolean(ndims), etchvtor_cache_boolean);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_boolean_check_value()
+ * check_value override for boolean validator
+ * returns external type of specified value
+ */
+int etchvtor_boolean_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ if (-1 == vtor->validate(vtor, value)) return -1;
+
+ if (vtor->numdimensions > 0)
+ *typecode_out = ETCH_XTRNL_TYPECODE_ARRAY;
+ else *typecode_out =((etch_boolean*)value)->value?
+ ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE: ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE;
+
+ return 0;
+}
+
+
+/**
+ * etchvtor_boolean_element_validator()
+ * element_validator override for boolean validator
+ * gets validator for array element or scalar
+ */
+etch_validator* etchvtor_boolean_element_validator(etch_validator* vtor)
+{
+ return etchvtor_boolean_get(vtor->numdimensions - 1);
+}
+
+
+/**
+ * new_validator_boolean()
+ * constructor for boolean validator
+ */
+etch_validator* new_validator_boolean(const int numdimensions)
+{
+ etch_validator* newvtor = NULL;
+ static char* mask = "bool[%d]"; char name[20];
+ sprintf(name,mask,numdimensions);
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_BOOL, ETCHTYPEB_PRIMITIVE,
+ CLASSID_PRIMITIVE_BOOL, 0, CLASSID_ARRAY_BOOL, numdimensions, name);
+
+ newvtor->check_value = etchvtor_boolean_check_value;
+ newvtor->element_validator = etchvtor_boolean_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * byte validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_byte /* byte validator cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_BYTE * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_byte_get()
+ * get a byte validator for specified dimensions
+ */
+etch_validator* etchvtor_byte_get(const int ndims)
+{
+ etch_validator* vtor = NULL;
+ if (-1 == etchvtor_check_dimensions(ndims)) return NULL;
+
+ if (ndims >= ETCHVTOR_MAX_CACHED)
+ vtor = new_validator_byte(ndims);
+ else
+ if (NULL == (vtor = etchvtor_cache_byte[ndims]))
+ vtor = etchvtor_cache_validator(
+ new_validator_byte(ndims), etchvtor_cache_byte);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_byte_validate()
+ * validate() override verifying value is in range of byte
+ */
+int etchvtor_byte_validate(etch_validator* vtor, etch_object* value)
+{
+ if (vtor->numdimensions > 0)
+ return etch_typevtor_validate(vtor, value);
+
+ if (NULL == value) return -1;
+
+ switch(((etch_object*)value)->class_id)
+ { case CLASSID_PRIMITIVE_BYTE:
+ return 0;
+ case CLASSID_PRIMITIVE_INT32:
+ return is_inrange_byte(((etch_int32*)value)->value)? 0: -1;
+ case CLASSID_PRIMITIVE_INT64:
+ return is_inrange_byte(((etch_int64*)value)->value)? 0: -1;
+ case CLASSID_PRIMITIVE_INT16:
+ return is_inrange_byte(((etch_int16*)value)->value)? 0: -1;
+ case CLASSID_PRIMITIVE_BOOL:
+ return is_inrange_bool(((etch_boolean*)value)->value)? 0: -1;
+ case CLASSID_PRIMITIVE_INT8:
+ return 0;
+ }
+
+ return -1;
+}
+
+
+/**
+ * etchvtor_byte_check_value()
+ * check_value override for byte validator
+ * returns external type of specified value
+ */
+int etchvtor_byte_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ if (-1 == vtor->validate(vtor, value)) return -1;
+
+ switch(vtor->numdimensions)
+ { case 0: *typecode_out = ETCH_XTRNL_TYPECODE_BYTE; break;
+ case 1: *typecode_out = ETCH_XTRNL_TYPECODE_BYTES; break;
+ default: *typecode_out = ETCH_XTRNL_TYPECODE_ARRAY;
+ }
+
+ return 0;
+}
+
+
+/**
+ * etchvtor_byte_validate_value()
+ * validate_value override for byte validator
+ */
+etch_object* etchvtor_byte_validate_value(etch_validator* vtor, etch_object* value)
+{
+ byte byteval = 0;
+
+ etch_object* valobj = etch_typevtor_validate_value(vtor, value);
+ if (NULL == valobj) return NULL;
+
+ /* here, the returned object is the passed object, caller does no cleanup */
+ if (vtor->numdimensions > 0 || is_etch_byte(value)) return value;
+
+ if (-1 == etchtagdata_byte_value (value, &byteval)) return NULL;
+
+ /* here, the returned object is not the same as the passed object,
+ * so caller must replace the reference, and destroy the passed object */
+ return (etch_object*) new_byte(byteval);
+}
+
+
+/**
+ * etchvtor_byte_element_validator()
+ * element_validator override for byte validator
+ * gets validator for array element or scalar
+ */
+etch_validator* etchvtor_byte_element_validator(etch_validator* vtor)
+{
+ return etchvtor_byte_get(vtor->numdimensions - 1);
+}
+
+
+/**
+ * new_validator_byte()
+ * constructor for byte validator
+ */
+etch_validator* new_validator_byte(const int numdimensions)
+{
+ etch_validator* newvtor = NULL;
+ static char* mask = "byte[%d]"; char name[20];
+ sprintf(name,mask,numdimensions);
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_BYTE, ETCHTYPEB_PRIMITIVE,
+ CLASSID_PRIMITIVE_BYTE, 0, CLASSID_ARRAY_BYTE, numdimensions, name);
+
+ newvtor->validate = etchvtor_byte_validate;
+ newvtor->check_value = etchvtor_byte_check_value;
+ newvtor->validate_value = etchvtor_byte_validate_value;
+ newvtor->element_validator = etchvtor_byte_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * int8 validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_int8 /* int8 validator cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_INT8 * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_int8_get()
+ * get a int8 validator for specified dimensions
+ */
+etch_validator* etchvtor_int8_get(const int ndims)
+{
+ etch_validator* vtor = NULL;
+ if (-1 == etchvtor_check_dimensions(ndims)) return NULL;
+
+ if (ndims >= ETCHVTOR_MAX_CACHED)
+ vtor = new_validator_int8(ndims);
+ else
+ if (NULL == (vtor = etchvtor_cache_int8[ndims]))
+ vtor = etchvtor_cache_validator(
+ new_validator_int8(ndims), etchvtor_cache_int8);
+ return vtor;
+}
+
+
+/**
+ * new_validator_int8()
+ * constructor for int8 validator
+ */
+etch_validator* new_validator_int8(const int numdimensions)
+{
+ etch_validator* newvtor = NULL;
+ static char* mask = "int8[%d]"; char name[20];
+ sprintf(name,mask,numdimensions);
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_INT8, ETCHTYPEB_PRIMITIVE,
+ CLASSID_PRIMITIVE_INT8, 0, CLASSID_ARRAY_INT8, numdimensions, name);
+
+ newvtor->validate = etchvtor_byte_validate;
+ newvtor->check_value = etchvtor_byte_check_value;
+ newvtor->validate_value = etchvtor_byte_validate_value;
+ newvtor->element_validator = etchvtor_byte_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * int16 validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+etch_validator** etchvtor_cache_int16 /* int16 validator cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_INT16 * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_int16_get()
+ * get a int16 validator for specified dimensions
+ */
+etch_validator* etchvtor_int16_get(const int ndims)
+{
+ etch_validator* vtor = NULL;
+ if (-1 == etchvtor_check_dimensions(ndims)) return NULL;
+
+ if (ndims >= ETCHVTOR_MAX_CACHED)
+ vtor = new_validator_int16(ndims);
+ else
+ if (NULL == (vtor = etchvtor_cache_int16[ndims]))
+ vtor = etchvtor_cache_validator(
+ new_validator_int16(ndims), etchvtor_cache_int16);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_int16_validate()
+ * validate() override verifying value is in range of short
+ */
+int etchvtor_int16_validate(etch_validator* vtor, etch_object* value)
+{
+ int result = -1;
+
+ if (vtor->numdimensions > 0)
+ result = etch_typevtor_validate(vtor, value);
+ else
+ if (NULL == value)
+ result = -1;
+ else switch(((etch_object*)value)->class_id)
+ { case CLASSID_PRIMITIVE_BYTE:
+ case CLASSID_PRIMITIVE_INT8:
+ case CLASSID_PRIMITIVE_INT16:
+ result = 0;
+ break;
+ case CLASSID_PRIMITIVE_INT32:
+ if (is_inrange_int16(((etch_int32*)value)->value))
+ result = 0;
+ break;
+ case CLASSID_PRIMITIVE_INT64:
+ if (is_inrange_int16(((etch_int64*)value)->value))
+ result = 0;
+ }
+
+ return result;
+}
+
+
+/**
+ * etchvtor_int16_check_value()
+ * check_value override for int16 validator
+ * returns external type of specified value
+ */
+int etchvtor_int16_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ short shortval = 0;
+ int result = vtor->validate(vtor, value);
+
+ if (-1 == result);
+ else
+ if (vtor->numdimensions)
+ *typecode_out = ETCH_XTRNL_TYPECODE_ARRAY;
+ else
+ if (-1 == (result = etchtagdata_int16_value(value, &shortval)));
+ else
+ if (is_inrange_byte(shortval))
+ *typecode_out = ETCH_XTRNL_TYPECODE_BYTE;
+ else *typecode_out = ETCH_XTRNL_TYPECODE_SHORT;
+
+ return result;
+}
+
+
+/**
+ * etchvtor_int16_validate_value()
+ * validate_value override for short int validator
+ */
+etch_object* etchvtor_int16_validate_value(etch_validator* vtor, etch_object* value)
+{
+ short shortval = 0;
+
+ etch_object* valobj = etch_typevtor_validate_value(vtor, value);
+ if (NULL == valobj) return NULL;
+
+ /* here, the returned object is the passed object, caller does no cleanup */
+ if (vtor->numdimensions > 0 || is_etch_int16(value)) return value;
+
+ if (-1 == etchtagdata_int16_value (value, &shortval)) return NULL;
+
+ /* here, the returned object is not the same as the passed object,
+ * so caller must replace the reference, and destroy the passed object */
+ return (etch_object*) new_int16(shortval);
+}
+
+
+/**
+ * etchvtor_int16_element_validator()
+ * element_validator override for int16 validator
+ * gets validator for array element or scalar
+ */
+etch_validator* etchvtor_int16_element_validator(etch_validator* vtor)
+{
+ return etchvtor_int16_get(vtor->numdimensions - 1);
+}
+
+
+/**
+ * new_validator_int16()
+ * constructor for int16 validator
+ */
+etch_validator* new_validator_int16(const int numdimensions)
+{
+ etch_validator* newvtor = NULL;
+ static char* mask = "int16[%d]"; char name[20];
+ sprintf(name,mask,numdimensions);
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_INT16, ETCHTYPEB_PRIMITIVE,
+ CLASSID_PRIMITIVE_INT16, 0, CLASSID_ARRAY_INT16, numdimensions, name);
+
+ newvtor->validate = etchvtor_int16_validate;
+ newvtor->check_value = etchvtor_int16_check_value;
+ newvtor->validate_value = etchvtor_int16_validate_value;
+ newvtor->element_validator = etchvtor_int16_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * int32 validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_int32 /* int32 validator cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_INT32 * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_int32_get()
+ * get a int32 validator for specified dimensions
+ */
+etch_validator* etchvtor_int32_get(const int ndims)
+{
+ etch_validator* vtor = NULL;
+ if (-1 == etchvtor_check_dimensions(ndims)) return NULL;
+
+ if (ndims >= ETCHVTOR_MAX_CACHED)
+ vtor = new_validator_int32(ndims);
+ else
+ if (NULL == (vtor = etchvtor_cache_int32[ndims]))
+ vtor = etchvtor_cache_validator(
+ new_validator_int32(ndims), etchvtor_cache_int32);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_int32_validate()
+ * validate() override verifying value is in range of int
+ */
+int etchvtor_int32_validate(etch_validator* vtor, etch_object* value)
+{
+ int result = -1;
+ if (vtor->numdimensions > 0)
+ result = etch_typevtor_validate(vtor, value);
+ else
+ if (NULL == value)
+ result = -1;
+ else switch(((etch_object*)value)->class_id)
+ { case CLASSID_PRIMITIVE_BYTE:
+ case CLASSID_PRIMITIVE_INT8:
+ case CLASSID_PRIMITIVE_INT16:
+ case CLASSID_PRIMITIVE_INT32:
+ result = 0;
+ break;
+ case CLASSID_PRIMITIVE_INT64:
+ if (is_inrange_int32(((etch_int64*)value)->value))
+ result = 0;
+ }
+
+ return result;
+}
+
+
+/**
+ * etchvtor_int32_validate_value()
+ * validate_value override for int validator
+ */
+etch_object* etchvtor_int32_validate_value(etch_validator* vtor, etch_object* value)
+{
+ int intval = 0;
+
+ etch_object* valobj = etch_typevtor_validate_value(vtor, value);
+ if (NULL == valobj) return NULL;
+
+ /* here, the returned object is the passed object, caller does no cleanup */
+ if (vtor->numdimensions > 0 || is_etch_int32(value)) return value;
+
+ if (-1 == etchtagdata_int32_value (value, &intval)) return NULL;
+
+ /* here, the returned object is not the same as the passed object,
+ * so caller must replace the reference, and destroy the passed object */
+ return (etch_object*) new_int32(intval);
+}
+
+
+/**
+ * etchvtor_int32_element_validator()
+ * element_validator override for int32 validator
+ * gets validator for array element or scalar
+ */
+etch_validator* etchvtor_int32_element_validator(etch_validator* vtor)
+{
+ return etchvtor_int32_get(vtor->numdimensions - 1);
+}
+
+
+/**
+ * etchvtor_int32_check_value()
+ * check_value override for int32 validator
+ * returns external type of specified value
+ */
+int etchvtor_int32_check_value(etch_validator* vtor, etch_object* value, byte* typecode_out)
+{
+ int intval = 0;
+
+ int result = vtor->validate(vtor, value);
+ if (-1 == result);
+ else
+ if (vtor->numdimensions)
+ *typecode_out = ETCH_XTRNL_TYPECODE_ARRAY;
+ else
+ if (-1 == (result = etchtagdata_int32_value(value, &intval)));
+ else
+ if (is_inrange_byte(intval))
+ *typecode_out = ETCH_XTRNL_TYPECODE_BYTE;
+ else
+ if (is_inrange_int16(intval))
+ *typecode_out = ETCH_XTRNL_TYPECODE_SHORT;
+ else *typecode_out = ETCH_XTRNL_TYPECODE_INT;
+
+ return result;
+}
+
+
+/**
+ * new_validator_int32()
+ * constructor for int32 validator
+ */
+etch_validator* new_validator_int32(const int numdimensions)
+{
+ etch_validator* newvtor = NULL;
+ static char* mask = "int32[%d]"; char name[20];
+ sprintf(name,mask,numdimensions);
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_INT32, ETCHTYPEB_PRIMITIVE,
+ CLASSID_PRIMITIVE_INT32, 0, CLASSID_ARRAY_INT32, numdimensions, name);
+
+ newvtor->validate = etchvtor_int32_validate;
+ newvtor->check_value = etchvtor_int32_check_value;
+ newvtor->validate_value = etchvtor_int32_validate_value;
+ newvtor->element_validator = etchvtor_int32_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * int64 validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_int64 /* int64 validator cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_INT64 * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_int64_get()
+ * get a int64 validator for specified dimensions
+ */
+etch_validator* etchvtor_int64_get(const int ndims)
+{
+ etch_validator* vtor = NULL;
+ if (-1 == etchvtor_check_dimensions(ndims)) return NULL;
+
+ if (ndims >= ETCHVTOR_MAX_CACHED)
+ vtor = new_validator_int64(ndims);
+ else
+ if (NULL == (vtor = etchvtor_cache_int64[ndims]))
+ vtor = etchvtor_cache_validator(
+ new_validator_int64(ndims), etchvtor_cache_int64);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_int64_validate()
+ * validate() override verifying value is in range of long long
+ */
+int etchvtor_int64_validate(etch_validator* vtor, etch_object* value)
+{
+ int result = -1;
+
+ if (vtor->numdimensions > 0)
+ result = etch_typevtor_validate(vtor, value);
+ else
+ if (NULL == value);
+ else switch(((etch_object*)value)->class_id)
+ { case CLASSID_PRIMITIVE_BYTE:
+ case CLASSID_PRIMITIVE_INT8:
+ case CLASSID_PRIMITIVE_INT16:
+ case CLASSID_PRIMITIVE_INT32:
+ case CLASSID_PRIMITIVE_INT64:
+ result = 0;
+ }
+ return result;
+}
+
+
+/**
+ * etchvtor_int64_validate_value()
+ * validate_value override for long validator
+ */
+etch_object* etchvtor_int64_validate_value(etch_validator* vtor, etch_object* value)
+{
+ int64 longval = 0;
+
+ etch_object* valobj = etch_typevtor_validate_value(vtor, value);
+ if (NULL == valobj) return NULL;
+
+ /* here, the returned object is the passed object, caller does no cleanup */
+ if (vtor->numdimensions > 0 || is_etch_int64(value)) return value;
+
+ if (-1 == etchtagdata_int64_value (value, &longval)) return NULL;
+
+ /* here, the returned object is not the same as the passed object,
+ * so caller must replace the reference, and destroy the passed object */
+ return (etch_object*) new_int64(longval);
+}
+
+
+/**
+ * etchvtor_int64_element_validator()
+ * element_validator override for int64 validator
+ * gets validator for array element or scalar
+ */
+etch_validator* etchvtor_int64_element_validator(etch_validator* vtor)
+{
+ return etchvtor_int64_get(vtor->numdimensions - 1);
+}
+
+
+/**
+ * etchvtor_int64_check_value()
+ * check_value override for int64 validator
+ * returns external type of specified value
+ */
+int etchvtor_int64_check_value(etch_validator* vtor, etch_object* value, byte* typecode_out)
+{
+ int64 longval = 0;
+
+ int result = vtor->validate(vtor, value);
+ if (-1 == result);
+ else
+ if (vtor->numdimensions)
+ *typecode_out = ETCH_XTRNL_TYPECODE_ARRAY;
+ else
+ if (-1 == (result = etchtagdata_int64_value(value, &longval)));
+ else
+ if (is_inrange_byte(longval))
+ *typecode_out = ETCH_XTRNL_TYPECODE_BYTE;
+ else
+ if (is_inrange_int16(longval))
+ *typecode_out = ETCH_XTRNL_TYPECODE_SHORT;
+ else
+ if (is_inrange_int32(longval))
+ *typecode_out = ETCH_XTRNL_TYPECODE_INT;
+ else *typecode_out = ETCH_XTRNL_TYPECODE_LONG;
+
+ return result;
+}
+
+
+/**
+ * new_validator_int64()
+ * constructor for int64 validator
+ */
+etch_validator* new_validator_int64(const int numdimensions)
+{
+ etch_validator* newvtor = NULL;
+ static char* mask = "int64[%d]"; char name[20];
+ sprintf(name,mask,numdimensions);
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_INT64, ETCHTYPEB_PRIMITIVE,
+ CLASSID_PRIMITIVE_INT64, 0, CLASSID_ARRAY_INT64, numdimensions, name);
+
+ newvtor->validate = etchvtor_int64_validate;
+ newvtor->check_value = etchvtor_int64_check_value;
+ newvtor->validate_value = etchvtor_int64_validate_value;
+ newvtor->element_validator = etchvtor_int64_element_validator;
+
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * float validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_float /* float validator cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_FLOAT * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_float_get()
+ * get a float validator for specified dimensions
+ */
+etch_validator* etchvtor_float_get(const int ndims)
+{
+ etch_validator* vtor = NULL;
+ if (-1 == etchvtor_check_dimensions(ndims)) return NULL;
+
+ if (ndims >= ETCHVTOR_MAX_CACHED)
+ vtor = new_validator_float(ndims);
+ else
+ if (NULL == (vtor = etchvtor_cache_float[ndims]))
+ vtor = etchvtor_cache_validator(
+ new_validator_float(ndims), etchvtor_cache_float);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_float_check_value()
+ * check_value override for float validator
+ * returns external type of specified value
+ */
+int etchvtor_float_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ if (-1 == vtor->validate(vtor, value)) return -1;
+ *typecode_out = (vtor->numdimensions)?
+ ETCH_XTRNL_TYPECODE_ARRAY: ETCH_XTRNL_TYPECODE_FLOAT;
+ return 0;
+}
+
+
+/**
+ * etchvtor_float_element_validator()
+ * element_validator override for float validator
+ * gets validator for array element or scalar
+ */
+etch_validator* etchvtor_float_element_validator(etch_validator* vtor)
+{
+ return etchvtor_float_get(vtor->numdimensions - 1);
+}
+
+
+/**
+ * new_validator_float()
+ * constructor for float validator
+ */
+etch_validator* new_validator_float(const int numdimensions)
+{
+ etch_validator* newvtor = NULL;
+ static char* mask = "float[%d]"; char name[20];
+ sprintf(name,mask,numdimensions);
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_FLOAT, ETCHTYPEB_PRIMITIVE,
+ CLASSID_PRIMITIVE_FLOAT, 0, CLASSID_ARRAY_FLOAT, numdimensions, name);
+
+ newvtor->check_value = etchvtor_float_check_value;
+ newvtor->element_validator = etchvtor_float_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * double validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_double /* double validator cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_DOUBLE * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_double_get()
+ * get a double validator for specified dimensions
+ */
+etch_validator* etchvtor_double_get(const int ndims)
+{
+ etch_validator* vtor = NULL;
+ if (-1 == etchvtor_check_dimensions(ndims)) return NULL;
+
+ if (ndims >= ETCHVTOR_MAX_CACHED)
+ vtor = new_validator_double(ndims);
+ else
+ if (NULL == (vtor = etchvtor_cache_double[ndims]))
+ vtor = etchvtor_cache_validator(
+ new_validator_double(ndims), etchvtor_cache_double);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_double_check_value()
+ * check_value override for double validator
+ * returns external type of specified value
+ */
+int etchvtor_double_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ if (-1 == vtor->validate(vtor, value)) return -1;
+
+ *typecode_out = (vtor->numdimensions)?
+ ETCH_XTRNL_TYPECODE_ARRAY: ETCH_XTRNL_TYPECODE_DOUBLE;
+ return 0;
+}
+
+
+/**
+ * etchvtor_double_element_validator()
+ * element_validator override for double validator
+ * gets validator for array element or scalar
+ */
+etch_validator* etchvtor_double_element_validator(etch_validator* vtor)
+{
+ return etchvtor_double_get(vtor->numdimensions - 1);
+}
+
+
+/**
+ * new_validator_double()
+ * constructor for double validator
+ */
+etch_validator* new_validator_double(const int numdimensions)
+{
+ etch_validator* newvtor = NULL;
+ static char* mask = "double[%d]"; char name[20];
+ sprintf(name,mask,numdimensions);
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_DOUBLE, ETCHTYPEB_PRIMITIVE,
+ CLASSID_PRIMITIVE_DOUBLE, 0, CLASSID_ARRAY_DOUBLE, numdimensions, name);
+
+ newvtor->check_value = etchvtor_double_check_value;
+ newvtor->element_validator = etchvtor_double_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * string validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_string /* string validator cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_STRING * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_string_get()
+ * get a string validator for specified dimensions
+ */
+etch_validator* etchvtor_string_get(const int ndims)
+{
+ etch_validator* vtor = NULL;
+ if (-1 == etchvtor_check_dimensions(ndims)) return NULL;
+
+ if (ndims >= ETCHVTOR_MAX_CACHED)
+ vtor = new_validator_string(ndims);
+ else
+ if (NULL == (vtor = etchvtor_cache_string[ndims]))
+ vtor = etchvtor_cache_validator(
+ new_validator_string(ndims), etchvtor_cache_string);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_string_check_value()
+ * check_value override for string validator
+ * returns external type of specified value
+ */
+int etchvtor_string_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ byte typecode = 0;
+ if (-1 == vtor->validate(vtor, value)) return -1;
+
+ if (vtor->numdimensions)
+ typecode = ETCH_XTRNL_TYPECODE_ARRAY;
+ else
+ if (((etch_string*)value)->char_count)
+ typecode = ETCH_XTRNL_TYPECODE_STRING;
+ else typecode = ETCH_XTRNL_TYPECODE_EMPTY_STRING;
+
+ if (typecode) *typecode_out = typecode;
+ return 0;
+}
+
+
+/**
+ * etchvtor_string_element_validator()
+ * element_validator override for string validator
+ * gets validator for array element or scalar
+ */
+etch_validator* etchvtor_string_element_validator(etch_validator* vtor)
+{
+ return etchvtor_string_get(vtor->numdimensions - 1);
+}
+
+
+/**
+ * new_validator_string()
+ * constructor for string validator
+ */
+etch_validator* new_validator_string(const int numdimensions)
+{
+ etch_validator* newvtor = NULL;
+ static char* mask = "string[%d]"; char name[20];
+ sprintf(name,mask,numdimensions);
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_STRING, ETCHTYPEB_PRIMITIVE,
+ CLASSID_STRING, 0, CLASSID_ARRAY_STRING, numdimensions, name);
+
+ newvtor->check_value = etchvtor_string_check_value;
+ newvtor->element_validator = etchvtor_string_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * object validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_object /* object validator cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_OBJECT * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_object_get()
+ * get a object validator for specified dimensions
+ */
+etch_validator* etchvtor_object_get(const int ndims)
+{
+ etch_validator* vtor = NULL;
+ if (-1 == etchvtor_check_dimensions(ndims)) return NULL;
+
+ if (ndims >= ETCHVTOR_MAX_CACHED)
+ vtor = new_validator_object(ndims);
+ else
+ if (NULL == (vtor = etchvtor_cache_object[ndims]))
+ vtor = etchvtor_cache_validator(
+ new_validator_object(ndims), etchvtor_cache_object);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_object_validate()
+ * any etch c object can be assigned to a scalar etch c object.
+ * this validation is necessary in the c code whereas it is not in the java version.
+ */
+int etchvtor_object_validate(etch_validator* vtor, etch_object* value)
+{
+ return value? 0: -1;
+}
+
+
+/**
+ * etchvtor_object_check_value()
+ * check_value override for object validator
+ * returns external type of specified value
+ */
+int etchvtor_object_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ if (-1 == vtor->validate(vtor, value)) return -1;
+
+ if (vtor->numdimensions)
+ *typecode_out = ETCH_XTRNL_TYPECODE_ARRAY;
+ else *typecode_out = ETCH_XTRNL_TYPECODE_ANY;
+
+ return 0;
+}
+
+
+/**
+ * etchvtor_object_element_validator()
+ * element_validator override for object validator
+ * gets validator for array element or scalar
+ */
+etch_validator* etchvtor_object_element_validator(etch_validator* vtor)
+{
+ return vtor->numdimensions? etchvtor_object_get(vtor->numdimensions - 1): vtor;
+}
+
+
+/**
+ * new_validator_object()
+ * constructor for object validator
+ */
+etch_validator* new_validator_object(const int numdimensions)
+{
+ etch_validator* newvtor = NULL;
+ static char* mask = "object[%d]"; char name[20];
+ sprintf(name,mask,numdimensions);
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_OBJECT, ETCHTYPEB_ETCHOBJECT,
+ CLASSID_OBJECT, 0, CLASSID_ARRAY_OBJECT, numdimensions, name);
+
+ newvtor->validate = etchvtor_object_validate;
+ newvtor->check_value = etchvtor_object_check_value;
+ newvtor->element_validator = etchvtor_object_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * exception validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_exception /* exception vtor cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_EXCEPTION * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_exception_get()
+ * get an exception validator
+ */
+etch_validator* etchvtor_exception_get()
+{
+ etch_validator* vtor = NULL;
+
+ if (NULL == (vtor = etchvtor_cache_exception[0]))
+ vtor = etchvtor_cache_validator(
+ new_validator_exception(), etchvtor_cache_exception);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_exception_check_value()
+ * check_value override for exception validator
+ * returns external type of specified value
+ */
+int etchvtor_exception_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ if (-1 == vtor->validate(vtor, (etch_object*) value)) return -1;
+ *typecode_out = ETCH_XTRNL_TYPECODE_CUSTOM;
+ return 0;
+}
+
+
+/**
+ * etchvtor_exception_element_validator()
+ */
+etch_validator* etchvtor_exception_element_validator(etch_validator* vtor)
+{
+ return etchvtor_exception_get(0);
+}
+
+
+/**
+ * new_validator_exception()
+ * constructor for exception validator
+ */
+etch_validator* new_validator_exception(const int numdimensions)
+{
+ etch_validator* newvtor = new_type_validator_1(CLASSID_VALIDATOR_EXCEPTION,
+ ETCHTYPEB_EXCEPTION, CLASSID_EXCEPTION, 0, CLASSID_ARRAY_OBJECT, 0, "excp");
+ newvtor->check_value = etchvtor_exception_check_value;
+ newvtor->element_validator = etchvtor_exception_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * eod object validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_validator** etchvtor_cache_eod /* eod vtor cache address */
+ = &etchvtor_cache[ETCHVTOR_CACHE_SLOT_EOD * ETCHVTOR_MAX_CACHED];
+
+/**
+ * etchvtor_eod_get()
+ * get an eod object validator
+ */
+etch_validator* etchvtor_eod_get()
+{
+ etch_validator* vtor = NULL;
+
+ if (NULL == (vtor = etchvtor_cache_eod[0]))
+ vtor = etchvtor_cache_validator(
+ new_validator_eod(), etchvtor_cache_eod);
+ return vtor;
+}
+
+
+/**
+ * etchvtor_eod_validate()
+ * validate() override
+ */
+int etchvtor_eod_validate(etch_validator* vtor, etch_object* value)
+{
+ return etchtagdata_is_eod(value)? 0: -1;
+}
+
+
+/**
+ * new_validator_eod()
+ * constructor for eod validator
+ */
+etch_validator* new_validator_eod(const int numdimensions)
+{
+ etch_validator* newvtor = new_type_validator_1(CLASSID_VALIDATOR_EOD,
+ ETCHTYPEB_NONE, CLASSID_NONE, 0, CLASSID_NONE, 0, "eod");
+ newvtor->validate = etchvtor_eod_validate;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * struct validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchvtor_struct_get()
+ * get a struct validator
+ */
+etch_validator* etchvtor_struct_get(etch_type* type, const int numdims)
+{
+ etch_validator* vtor = new_validator_struct(type, numdims);
+ /* caller must free this validator since is_cached is false */
+ return vtor;
+}
+
+
+/**
+ * etchvtor_struct_validate()
+ * validate() override verifying value is struct of expected type
+ */
+int etchvtor_struct_validate(etch_validator* vtor, etch_object* value)
+{
+ int result = etch_typevtor_validate(vtor, value);
+ if (result == -1);
+ else
+ if (vtor->numdimensions)
+ if (is_etch_nativearray(value));
+ else result = -1;
+ else
+ if (is_etch_struct(value))
+ if (is_equal_types(vtor->struct_type,
+ ((etch_structvalue*)value)->struct_type));
+ else result = -1;
+ else result = -1;
+
+ return result;
+}
+
+
+/**
+ * etchvtor_struct_check_value()
+ * check_value override for struct validator
+ * returns external type of specified value
+ */
+int etchvtor_struct_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ if (-1 == vtor->validate(vtor, (etch_object*) value)) return -1;
+ *typecode_out = vtor->numdimensions?
+ ETCH_XTRNL_TYPECODE_ARRAY: ETCH_XTRNL_TYPECODE_CUSTOM;
+ return 0;
+}
+
+
+/**
+ * etchvtor_struct_element_validator()
+ */
+etch_validator* etchvtor_struct_element_validator(etch_validator* vtor)
+{
+ return etchvtor_struct_get(vtor->struct_type, vtor->numdimensions - 1);
+}
+
+
+/**
+ * new_validator_struct()
+ * constructor for struct validator
+ */
+etch_validator* new_validator_struct (etch_type* type, const int numdimensions)
+{
+ char namebuf[64] = "struct_", ascbuf[64], dimbuf[8], *abuf = ascbuf;
+ etch_validator* newvtor = NULL;
+ if (!type || !is_good_type(type)) return NULL;
+
+ // TODO: pool
+ etch_encoding_transcode_wchar(&abuf, ETCH_ENCODING_UTF8, type->name, NULL);
+
+ strcat(namebuf, abuf); strcat(namebuf, "[");
+ sprintf(dimbuf, "%d", numdimensions);
+ strcat(namebuf, dimbuf); strcat(namebuf, "]");
+ etch_free(abuf); /* todo change id_name to ascii */
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_STRUCT, ETCHTYPEB_STRUCTVAL,
+ CLASSID_STRUCTVALUE, 0, CLASSID_ARRAY_OBJECT, numdimensions, namebuf);
+
+ newvtor->struct_type = type;
+
+ newvtor->validate = etchvtor_struct_validate;
+ newvtor->check_value = etchvtor_struct_check_value;
+ newvtor->element_validator = etchvtor_struct_element_validator;
+ return newvtor;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * custom validator
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchvtor_custom_get()
+ * get a custom validator
+ */
+etch_validator* etchvtor_custom_get (const unsigned short obj_type,
+ const unsigned short class_id, etch_type* type, const int numdims)
+{
+ etch_validator* vtor = new_validator_custom (obj_type, class_id, type, numdims);
+ /* caller must free this validator since is_cached is false */
+ return vtor;
+}
+
+
+/**
+ * etchvtor_custom_validate()
+ * validate() override verifying value is of expected type
+ */
+int etchvtor_custom_validate (etch_validator* vtor, etch_object* value)
+{
+ int result = etch_typevtor_validate(vtor, value);
+
+ return result;
+}
+
+
+/**
+ * etchvtor_struct_check_value()
+ * check_value override for struct validator
+ * returns external type of specified value.
+ */
+int etchvtor_custom_check_value(etch_validator* vtor,
+ etch_object* value, byte* typecode_out)
+{
+ if (-1 == vtor->validate(vtor, (etch_object*) value)) return -1;
+ *typecode_out = vtor->numdimensions?
+ ETCH_XTRNL_TYPECODE_ARRAY: ETCH_XTRNL_TYPECODE_CUSTOM;
+ return 0;
+}
+
+
+/**
+ * etchvtor_custom_element_validator()
+ */
+etch_validator* etchvtor_custom_element_validator (etch_validator* vtor)
+{
+ return etchvtor_custom_get(vtor->expected_obj_type, vtor->expected_class_id, vtor->struct_type, vtor->numdimensions - 1);
+}
+
+
+/**
+ * new_validator_custom()
+ * constructor for custom validator
+ */
+etch_validator* new_validator_custom (const unsigned short obj_type,
+ const unsigned short class_id, etch_type* type, const int numdims)
+{
+ char namebuf[64] = "custom_", ascbuf[64], dimbuf[8], *abuf = ascbuf;
+ etch_validator* newvtor = NULL;
+ if (!type || !is_good_type(type)) return NULL;
+ // TODO: pool
+ etch_encoding_transcode_wchar(&abuf, ETCH_ENCODING_UTF8, type->name, NULL);
+ strcat(namebuf, abuf); strcat(namebuf, "[");
+ sprintf(dimbuf, "%d", numdims);
+ strcat(namebuf, dimbuf); strcat(namebuf, "]");
+ etch_free(abuf); /* todo change id_name to ascii */
+
+ newvtor = new_type_validator_1(CLASSID_VALIDATOR_CUSTOM, obj_type, class_id, 0, CLASSID_ARRAY_OBJECT, numdims, namebuf);
+
+ newvtor->struct_type = type;
+
+ newvtor->validate = etchvtor_custom_validate;
+ newvtor->check_value = etchvtor_custom_check_value;
+ newvtor->element_validator = etchvtor_custom_element_validator;
+ return newvtor;
+}
+
+
+
+
+
+
diff --git a/binding-c/runtime/c/src/main/bindings/msg/etch_value_factory.c b/binding-c/runtime/c/src/main/bindings/msg/etch_value_factory.c
new file mode 100644
index 0000000..ccd7d46
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/msg/etch_value_factory.c
@@ -0,0 +1,169 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_valufact.c
+ * value factory
+ */
+
+#include "etch_value_factory.h"
+#include "etch_tagdata_out.h"
+#include "etch_cache.h"
+#include "etch_message.h"
+#include "etch_objecttypes.h"
+
+/**
+ * value factory virtual function default implementations
+ */
+
+etch_type* vf_add_type (void* vf, etch_type* t)
+{
+ return NULL;
+}
+
+
+etch_type* vf_get_type_by_id (void* vf, const unsigned id)
+{
+ return NULL;
+}
+
+
+etch_type* vf_get_type_by_name (void* vf, const wchar_t* name)
+{
+ return NULL;
+}
+
+
+etch_arraylist* vf_get_types (void* vf)
+{
+ return NULL;
+}
+
+
+wchar_t* vf_get_string_encoding (void* vf)
+{
+ return NULL;
+}
+
+
+etch_int64* vf_get_message_id (void* vf, etch_message* msgobj)
+{
+ return NULL;
+}
+
+
+int vf_set_message_id (void* vf, etch_message* msgobj, etch_int64* msgid)
+{
+ return 0;
+}
+
+
+etch_int64* vf_get_in_reply_to (void* vf, etch_message* msgobj)
+{
+ return NULL;
+}
+
+
+int vf_set_in_reply_to (void* vf, etch_message* msgobj, etch_int64* msgid)
+{
+ return 0;
+}
+
+
+etch_structvalue* vf_export_custom_value (void* vf, etch_object* value)
+{
+ return NULL;
+}
+
+
+etch_object* vf_import_custom_value (void* vf, etch_structvalue* svobj)
+{
+ return NULL;
+}
+
+
+etch_type* vf_get_custom_struct_type (void* vf, const unsigned etchclass)
+{
+ return NULL;
+}
+
+
+etch_type* vf_get_mt_exception (void* vf)
+{
+ return NULL;
+}
+
+
+int destroy_valuefactory(void* data)
+{
+ etch_value_factory* vf = (etch_value_factory*)data;
+
+ if (!is_etchobj_static_content(vf)) {
+ etch_object_destroy(vf->impl);
+ }
+
+ return destroy_objectex((etch_object*)vf);
+}
+
+/**
+ * new_value_factory()
+ * value factory constructor
+ */
+etch_value_factory* new_value_factory(const int objlen)
+{
+ etch_value_factory* vf = NULL;
+ i_value_factory* vtab = NULL;
+ const unsigned short CLASS_ID = CLASSID_VALUEFACTORY;
+ int objsize = objlen? objlen: sizeof(etch_value_factory);
+
+ vf = (etch_value_factory*) new_object(objsize, ETCHTYPEB_VALUEFACTORY, CLASSID_VALUEFACTORY);
+ ((etch_object*)vf)->destroy = destroy_valuefactory;
+ ((etch_object*)vf)->clone = clone_null;
+
+ vtab = etch_cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+ if(!vtab)
+ {
+ vtab = new_vtable(NULL, sizeof(i_value_factory), CLASS_ID);
+
+ /* default virtual function implementations */
+ vtab->add_type = vf_add_type;
+ vtab->export_custom_value = vf_export_custom_value;
+ vtab->get_in_reply_to = vf_get_in_reply_to;
+ vtab->get_message_id = vf_get_message_id;
+ vtab->get_string_encoding = vf_get_string_encoding;
+ vtab->get_type_by_id = vf_get_type_by_id;
+ vtab->get_type_by_name = vf_get_type_by_name;
+ vtab->get_types = vf_get_types;
+ vtab->import_custom_value = vf_import_custom_value;
+ vtab->set_in_reply_to = vf_set_in_reply_to;
+ vtab->set_message_id = vf_set_message_id;
+ vtab->get_custom_struct_type= vf_get_custom_struct_type;
+ vtab->get_mt_exception = vf_get_mt_exception;
+
+ etch_cache_insert(((etch_object*)vtab)->get_hashkey(vtab), vtab, FALSE);
+ }
+
+ ((etch_object*)vf)->vtab = (vtabmask*)vtab;
+ return vf;
+}
+
+
+
+
+
diff --git a/binding-c/runtime/c/src/main/bindings/support/etch_resources.c b/binding-c/runtime/c/src/main/bindings/support/etch_resources.c
new file mode 100644
index 0000000..ffd22df
--- /dev/null
+++ b/binding-c/runtime/c/src/main/bindings/support/etch_resources.c
@@ -0,0 +1,170 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_resources.c
+ */
+
+#include "etch_resources.h"
+#include "etch_map.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+
+
+
+const wchar_t* ETCH_RESXKEY_SOCKET = L"SOCKET";
+const wchar_t* ETCH_RESXKEY_ACCEPTED_CONX = L"ACCEPTED_CONX";
+const wchar_t* ETCH_RESXKEY_POOLTYPE_FREE = L"FREE_POOL";
+const wchar_t* ETCH_RESXKEY_POOLTYPE_QUEUED = L"QUEUED_POOL";
+const wchar_t* ETCH_RESXKEY_MSGIZER_FORMAT = L"Messagizer.format";
+const wchar_t* ETCH_RESXKEY_MSGIZER_VALUFACT= L"Messagizer.valueFactory";
+const wchar_t* ETCH_RESXVAL_XPORTFMT_BINARY = L"binary";;
+const wchar_t* ETCH_RESXVAL_XPORTFMT_XML = L"xml";
+const wchar_t* ETCH_XFACTKEY_TCP = L"tcp";
+const wchar_t* ETCH_XPORTKEY_START = L"START";
+const wchar_t* ETCH_XPORTKEY_IS_UP = L"IS_UP";
+const wchar_t* ETCH_XPORTKEY_STOP = L"STOP";
+const wchar_t* ETCH_XPORTKEY_START_AND_WAIT_UP = L"START_AND_WAIT_UP";
+const wchar_t* ETCH_XPORTKEY_STOP_AND_WAIT_DOWN = L"STOP_AND_WAIT_DOWN";
+
+// TODO - lose the above strings, replacing them with integer constants
+
+const int ETCH_RESKEY_SOCKET = 0x100;
+const int ETCH_RESKEY_POOLTYPE_FREE = 0x101;
+const int ETCH_RESKEY_POOLTYPE_QUEUED = 0x102;
+const int ETCH_RESKEY_MSGIZER_FORMAT = 0x104;
+const int ETCH_RESKEY_MSGIZER_VALUFACT = 0x105;
+const int ETCH_RESVAL_XPORTFMT_BINARY = 0x140;
+const int ETCH_RESVAL_XPORTFMT_XML = 0x141;
+const int ETCH_XPKEY_TCP = 0x142;
+const int ETCH_XPKEY_START = 0x160;
+const int ETCH_XPKEY_IS_UP = 0x161;
+const int ETCH_XPKEY_STOP = 0x162;
+const int ETCH_XPKEY_START_AND_WAITUP = 0x163;
+const int ETCH_XPKEY_STOP_AND_WAITDOWN = 0x164;
+const int ETCH_XPKEY_XPORTFMT_XML = 0x165;
+
+// TODO - I don't think we need these at all, we should use class_id
+// since in this way the object can be identified and we don't need
+// to assume that the enclosing object is an etch_int32. however not
+// all of these IDs are objects, some are table IDs
+
+const wchar_t* ETCH_RESXKEY_SOCKET;
+const wchar_t* ETCH_RESXKEY_ACCEPTED_CONX;
+const wchar_t* ETCH_RESXKEY_POOLTYPE_FREE;
+const wchar_t* ETCH_RESXKEY_POOLTYPE_QUEUED;
+const wchar_t* ETCH_RESXKEY_MSGIZER_FORMAT;
+const wchar_t* ETCH_RESXKEY_MSGIZER_VALUFACT;
+const wchar_t* ETCH_RESXVAL_XPORTFMT_BINARY;
+const wchar_t* ETCH_RESXVAL_XPORTFMT_XML;
+const wchar_t* ETCH_XFACTKEY_TCP;
+const wchar_t* ETCH_XPORTKEY_START;
+const wchar_t* ETCH_XPORTKEY_START_AND_WAIT_UP;
+const wchar_t* ETCH_XPORTKEY_IS_UP;
+const wchar_t* ETCH_XPORTKEY_STOP;
+const wchar_t* ETCH_XPORTKEY_STOP_AND_WAIT_DOWN;
+
+
+/*
+ * new_etch_resources
+ * etch_resources constructor
+ */
+etch_resources* new_etch_resources(const int initialsize)
+{
+ etch_resources* resx = new_hashtable(initialsize);
+ resx->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+ resx->content_obj_type = ETCHTYPEB_ETCHOBJECT;
+ resx->content_class_id = CLASSID_ANY;
+ resx->is_readonly_keys = resx->is_readonly_values = FALSE;
+ resx->freehook = string_to_object_clear_handler; /* frees memory on clear */
+ return resx;
+}
+
+
+/*
+ * etch_resources_add()
+ * adds specified key/value pair to specified resource map
+ */
+int etch_resources_add (etch_resources* resources, const wchar_t* key, etch_object* val)
+{
+ const int hashkey = etchmap_insertxw(resources, (wchar_t*) key, val, TRUE);
+ return hashkey? 0: -1;
+}
+
+
+/*
+ * etch_resources_get()
+ */
+etch_object* etch_resources_get (etch_resources* resources, const wchar_t* key)
+{
+ etch_object* founditem = etchmap_findxw(resources, (wchar_t*) key, NULL);
+ return founditem;
+}
+
+
+/*
+ * etch_resources_clear()
+ * clear a resources map, assuming all objects are owned unless marked as a static resource.
+ * @return count of items both removed from the map and freed.
+ */
+int etch_resources_clear (etch_resources* resources)
+{ // TODO lose this method or augment it to delete the hashtable entry memory also
+ // it leaves the memory the hashtable allocated for the key intact
+ int freedcount = 0;
+ etch_iterator iterator;
+ wchar_t* thiskey = NULL;
+ if (!is_etch_hashtable(resources)) return 0;
+ set_iterator(&iterator, resources, &resources->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ etch_object* value = iterator.current_value;
+
+ if (value && !is_etchobj_static_resource(value))
+ {
+ etch_object_destroy(value);
+ freedcount++;
+ }
+
+ thiskey = iterator.current_key;
+ etch_free(thiskey);
+
+ iterator.next(&iterator);
+ }
+
+ return freedcount;
+}
+
+
+/*
+ * etch_resources_replace()
+ * adds specified key/value pair to specified resource map, first removing prior item.
+ */
+int etch_resources_replace (etch_resources* resources, const wchar_t* key, etch_object* val)
+{
+ if (etch_resources_get (resources, key))
+ {
+ etch_object* removedobj = etchmap_delxw (resources, (wchar_t*) key);
+
+ if (removedobj)
+ if (!is_etchobj_static_resource(removedobj))
+ etch_object_destroy(removedobj);
+ }
+
+ return etch_resources_add (resources, key, val);
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_arraylist.c b/binding-c/runtime/c/src/main/common/etch_arraylist.c
new file mode 100644
index 0000000..aeaa243
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_arraylist.c
@@ -0,0 +1,789 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_arraylist.c -- implementation of arraylist.
+ */
+
+#include "etch_arraylist.h"
+#include "etch_mutex.h"
+#include "etch_mem.h"
+#include "etch_objecttypes.h"
+
+extern etch_pool_t* g_etch_main_pool;
+
+static int _destroy_arraylist(void*);
+
+/**
+ * new_etch_arraylist()
+ * constructor for an etch_arraylist, which implements i_iterable.
+ * passed initial size, and initial expansion delta, both expressed as
+ * number of entries.
+ */
+etch_arraylist* new_etch_arraylist(const unsigned int initsize, const unsigned int deltsize)
+{
+ etch_arraylist* list = (etch_arraylist*) new_object(sizeof(etch_arraylist), ETCHTYPEB_ARRAYLIST, CLASSID_ETCH_ARRAYLIST);
+
+ list->size = initsize > 0? initsize: ETCHARRAYLIST_DEFSIZE;
+ list->delta = deltsize > 0? deltsize: list->size;
+ list->size *= sizeof(void**); list->delta *= sizeof(void**);
+ list->base = etch_malloc(list->size, ETCHTYPEB_BYTES);
+ memset(list->base, 0, list->size);
+
+ ((etch_object*)list)->destroy = _destroy_arraylist;
+
+ new_iterable(&list->iterable, NULL,
+ etch_arraylist_iterable_first,
+ etch_arraylist_iterable_next,
+ etch_arraylist_iterable_has_next);
+
+ return list;
+}
+
+
+/**
+ * new_arraylist_from()
+ */
+etch_arraylist* new_etch_arraylist_from(etch_arraylist* thatlist)
+{
+ etch_arraylist* thislist = new_etch_arraylist(thatlist->size, thatlist->delta);
+
+ etch_arraylist_copyattrs(thislist, thatlist);
+
+ memcpy(thislist->base, thatlist->base, thatlist->count * sizeof(void*));
+ thislist->count = thatlist->count;
+
+ return thislist;
+}
+
+/**
+ * new_arraylist_synchronized()
+ * constructor for an etch_arraylist, which implements i_iterable.
+ * passed initial size, and initial expansion delta, both expressed as
+ * number of entries.
+ */
+etch_arraylist* new_etch_arraylist_synchronized(const unsigned int initialsize, const unsigned int deltasize)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_arraylist* arraylist = NULL;
+
+ arraylist = new_etch_arraylist(initialsize, deltasize);
+
+ // TODO: pool
+ status = etch_mutex_create(&((etch_object*)arraylist)->synclock, ETCH_MUTEX_NESTED, NULL);
+ if(status != ETCH_SUCCESS) {
+ // errror
+ }
+ return arraylist;
+}
+
+/**
+ * new_etch_arraylist_synchronized_from()
+ */
+etch_arraylist* new_etch_arraylist_synchronized_from(etch_arraylist* thatlist)
+{
+ etch_arraylist* thislist = new_etch_arraylist_synchronized(thatlist->size, thatlist->delta);
+
+ etch_arraylist_copyattrs(thislist, thatlist);
+
+ memcpy(thislist->base, thatlist->base, thatlist->count * sizeof(void*));
+ thislist->count = thatlist->count;
+
+ return thislist;
+}
+
+/**
+ * etcharraylist_destroy()
+ * destructor for an etch_arraylist.
+ * destroys the underlying list, the list shell, and the list content if requested.
+ * note that arraylist always owns its buffer and mutex. setting static content on
+ * an arraylist object applies only to the array content objects, if indeed content
+ * consists of object. if content is other than object, static content has no effect
+ * since the content buffer is freed regardless.
+ */
+void etch_arraylist_destroy(etch_arraylist* list, const int is_free_content)
+{
+ etch_status_t status = ETCH_SUCCESS;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ // disallow multiple destroy
+ status = etch_mutex_trylock(((etch_object*)list)->synclock);
+ if(status != ETCH_SUCCESS) {
+ return;
+ }
+ }
+
+
+ if (!is_etchobj_static_content(list)) /* clear and free content */
+ etch_arraylist_clear(list, is_free_content);
+
+ etch_free(list->base); /* arraylist always owns its buffer */
+
+ if(((etch_object*)list)->synclock) {
+ // release and free mutex
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ status = etch_mutex_destroy(((etch_object*)list)->synclock);
+
+ }
+
+ if (!is_etchobj_static_shell(list))
+ etch_free(list); /* free list object */
+}
+
+
+static int _destroy_arraylist(void* data)
+{
+ etch_arraylist* list = data;
+ etch_arraylist_destroy(list, !list->is_readonly);
+ return 0;
+}
+
+
+
+/* - - - - - - - - - -
+ * list maintenance
+ * - - - - - - - - - -
+ */
+
+/**
+ * _arraylist_realloc()
+ * private method to resize an arraylist.
+ * if a size greater than current size is specified, the list is realloced to that size,
+ * otherwise it is realloced to current size plus the delta size specified at construction.
+ * @note presumed that this is invoked only via _arraylist_checksize, thus synchronized.
+ */
+int _arraylist_realloc(etch_arraylist* list, const unsigned size)
+{
+ unsigned int copylength = 0, newsize = 0;
+ void* newbase = NULL;
+
+ newsize = size > list->size? size: list->size + list->delta;
+ newbase = etch_malloc(newsize, ETCHTYPEB_BYTES);
+ memset(newbase, 0, newsize);
+
+ copylength = list->count * sizeof(void**);
+ memcpy(newbase, list->base, copylength);
+
+ etch_free(list->base);
+ list->base = newbase;
+ list->size = newsize;
+ return 0;
+}
+
+
+/**
+ * _arraylist_checksize()
+ * private method ensures list has space required for pending content insertion.
+ */
+int _arraylist_checksize(etch_arraylist* list, const int pendingitems, const int is_locked)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = 1;
+ unsigned currsize = 0, newspace = pendingitems * sizeof(void**);
+
+ if(((etch_object*)list)->synclock != NULL && !is_locked) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ currsize = list->count * sizeof(void**);
+
+ if (currsize + newspace > list->size)
+ _arraylist_realloc(list, newspace > list->delta? newspace: 0);
+ else
+ result = 0;
+
+ if(((etch_object*)list)->synclock != NULL && !is_locked) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/*
+ * arraylist_clear()
+ * remove all content from the arraylist, freeing content memory only if requested.
+ * note that the list buffer does not shrink, rather retaining its current size.
+ */
+void etch_arraylist_clear(etch_arraylist* list, const int is_free_content)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ void** p = NULL;
+ arraycallback callback = NULL;
+ int items = 0, is_obj_content = 0, i = 0, freehandled = 0;
+ if (NULL == list || NULL == list->base) return;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ is_obj_content = list->content_type == ETCHARRAYLIST_CONTENT_OBJECT;
+ callback = list->freehook;
+ items = list->count;
+ p = list->base;
+
+ if (is_free_content && !list->is_readonly)
+ for(; i < (const int) items; i++, p++)
+ {
+ /* optional callback to handle free */
+ freehandled = callback? callback(i, *p): FALSE;
+
+ /* if we've marked the array as having etch object content,
+ * we invoke the object's destructor. */
+ if (freehandled);
+ else
+ if (is_obj_content)
+ ((etch_object*)*p)->destroy(*p);
+ else etch_free(*p);
+ }
+
+ memset(list->base, 0, items * sizeof(void**));
+ list->count = 0;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+}
+
+
+/* - - - - - - - - - -
+ * list api
+ * - - - - - - - - - -
+ */
+
+/*
+ * arraylist_add_from()
+ * add entries to the end of the list.
+ * returns count if items entered, or -1 if error.
+ * note that if the target array is not marked read only, this method must be
+ * used with great caution. since an arraylist not so marked will attempt to
+ * destroy its content when destroyed, and since we are copying memory references
+ * from one array to another here, the second destructor would attempt to free
+ * memory already freed in the first destructor. so to be safe, this method
+ * should be used only with arrays marked is_readonly = TRUE, and content_type =
+ * ETCHARRAYLIST_CONTENT_OBJECT.
+ */
+int etch_arraylist_add_from(etch_arraylist* list, etch_arraylist* newitems, etch_comparator compare)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int count = 0;
+ etch_iterator iterator;
+ if (!list || !list->base) return -1;
+ if (newitems->count == 0) return 0;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ set_iterator(&iterator, newitems, &newitems->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ if (etch_arraylist_contains(list, iterator.current_value, 0, compare))
+ continue;
+
+ _arraylist_checksize(list, 1, TRUE);
+
+ list->base[list->count++] = iterator.current_value;
+ count++;
+
+ iterator.next(&iterator);
+ }
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return count;
+}
+
+
+/*
+ * etch_arraylist_add()
+ * add an entry to the end of the list.
+ */
+int etch_arraylist_add(etch_arraylist* list, void* content)
+{
+ etch_status_t status = ETCH_SUCCESS;
+
+ if (!list || !list->base) return -1;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ _arraylist_checksize(list, 1, TRUE);
+
+ list->base[list->count++] = content;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return 0;
+}
+
+
+/*
+ * etch_arraylist_insert()
+ * add a node anywhere in the list, returning 0 if OK, or -1 if error.
+ * this implementation cannot insert past the current end of list,
+ * in other words to insert at index n, there must be at least n
+ * entries currently in the list (list->count >= n), that is to say,
+ * the insertion must expand the size of the list by exactly one slot.
+ */
+int etch_arraylist_insert(etch_arraylist* list, const unsigned int i, void* content)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ unsigned shiftcount = 0, result = -1, j = 0;
+ void **p = NULL, **q = NULL;
+ if ((NULL == list) || (i < 0)) return -1;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ _arraylist_checksize(list, 1, TRUE);
+
+ if (i <= list->count)
+ {
+ shiftcount = list->count - i; /* shift higher entries up */
+ p = &list->base[list->count - 1];
+ q = &list->base[list->count];
+
+ for(j = 0; j < (const unsigned) shiftcount; j++, p--, q--)
+ *q = *p;
+
+ list->base[i] = content;
+ ++list->count;
+ result = 0;
+ }
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/*
+ * etch_arraylist_containsp()
+ * return 1 or 0 indicating if the list contains the supplied content pointer.
+ */
+int etch_arraylist_containsp(etch_arraylist* list, void* content, const unsigned startat)
+{
+ return etch_arraylist_indexofp(list, content, startat) == -1? FALSE: TRUE;
+}
+
+/*
+ * etch_arraylist_count()
+ * return the number of elements or -1 if list is NULL
+ */
+int etch_arraylist_count(etch_arraylist* list)
+{
+ if (NULL==list)
+ return -1;
+ else
+ return list->count;
+}
+
+/*
+ * etch_arraylist_indexofp()
+ * if the list contains the supplied content pointer, return its index;
+ * return -1 if not found or if a parameter was in error.
+ */
+int etch_arraylist_indexofp(etch_arraylist* list, void* content, const unsigned startat)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ void** p = NULL;
+ int result = -1;
+ unsigned int i = startat;
+ if ((NULL == list) || (i < 0)) return result;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (i < list->count)
+ {
+ p = &list->base[i];
+
+ for(; i < list->count; i++, p++)
+ {
+ if (*p == content)
+ { result = i;
+ break;
+ }
+ }
+ }
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/*
+ * arraylist_contains()
+ * return 1 or 0 indicating if the list contains the supplied content.
+ * caller must supply a comparator function int (*f)(void* this, void* that);
+ * which should return -2 bad params, -1 compares less, 0 equal, 1 greater.
+ */
+int etch_arraylist_contains(etch_arraylist* list, void* content, const unsigned startat,
+ etch_comparator compare)
+{
+ return etch_arraylist_indexof(list, content, startat, compare) == -1? FALSE: TRUE;
+}
+
+
+/*
+ * etch_arraylist_indexof()
+ * if the list contains the supplied content, return its index.
+ * return -1 if a parameter was in error.
+ * caller must supply a comparator function int (*f)(void* this, void* that);
+ * which should return -2 bad params, -1 compares less, 0 equal, 1 greater.
+ */
+int etch_arraylist_indexof(etch_arraylist* list,
+ void* content, const unsigned startat, etch_comparator compare)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ void** p = NULL;
+ unsigned i = startat;
+ int result = -1;
+ if ((NULL == list) || (i < 0)) return result;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (i < list->count)
+ {
+ p = &list->base[i];
+
+ for(; i < list->count; i++, p++)
+ {
+ if (0 == compare(content, *p))
+ { result = i;
+ break;
+ }
+ }
+ }
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/*
+ * etch_arraylist_set()
+ * replace content at specified index position.
+ */
+int etch_arraylist_set (etch_arraylist* list, const unsigned i, void* content)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = -1;
+ if ((NULL == list) || (i < 0)) return -1;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (i < list->count)
+ {
+ list->base[i] = content;
+ result = 0;
+ }
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/*
+ * etch_arraylist_get()
+ * return content at specified index position, or NULL if parameter error.
+ * a non-disposable reference is returned.
+ * todo: mark arraylist content objects static, such that destroy() is benign,
+ * unlocking them only at such time as the arraylist itself is cleared or
+ * destroyed, or a remove() with is_free_content is requested.
+ */
+void* etch_arraylist_get (etch_arraylist* list, const unsigned i)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ void* result = NULL;
+ if ((NULL == list) || (i < 0)) return NULL;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (i < list->count)
+ result = list->base[i];
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/*
+ * etch_arraylist_remove()
+ * remove entry at specified index position, freeing content memory if requested.
+ * return -1 if a parameter was in error, or zero if OK.
+ */
+int etch_arraylist_remove (etch_arraylist* list, const unsigned i, const int is_free_content)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ unsigned j = 0, newcount = 0, is_obj_content = 0, freehandled = 0, result = -1;
+ arraycallback callback = NULL;
+ void *content = NULL, **p = NULL, **q = NULL;
+ if ((NULL == list) || (i < 0)) return -1;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (i < list->count)
+ {
+ is_obj_content = list->content_type == ETCHARRAYLIST_CONTENT_OBJECT;
+ callback = list->freehook;
+
+ if (is_free_content && !list->is_readonly)
+ {
+ if (content = list->base[i])
+ {
+ freehandled = callback? callback(i, content): FALSE;
+ if (freehandled);
+ else
+ if (is_obj_content)
+ ((etch_object*)content)->destroy(content);
+ else etch_free(content);
+ }
+ }
+
+ newcount = list->count - 1;
+ p = &list->base[i]; /* shift higher entries down */
+ q = &list->base[i + 1]; /* we eschew platform-dependent memmove() */
+
+ for(j = i; j < (const unsigned)newcount; j++, p++, q++)
+ *p = *q;
+
+ list->base[newcount] = NULL; /* zero the now-extra slot */
+ list->count = newcount;
+ result = 0;
+ }
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/*
+ * arraylist_remove_content()
+ * remove entry whose value is specified.
+ */
+int etch_arraylist_remove_content(etch_arraylist* list,
+ void* content, const unsigned startat, etch_comparator compare)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = -1, index = 0;
+ if (NULL == list) return result;
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (-1 != (index = etch_arraylist_indexof(list, content, startat, compare)))
+ result = etch_arraylist_remove(list, index, TRUE);
+
+ if(((etch_object*)list)->synclock != NULL) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/*
+ * etch_arraylist_copyattrs()
+ * copy array attributes from one array to another
+ */
+void etch_arraylist_copyattrs(etch_arraylist* to, etch_arraylist* from)
+{
+ ((etch_object*)to)->class_id = ((etch_object*)from)->class_id;
+ to->content_obj_type = from->content_obj_type;
+ to->content_class_id = from->content_class_id;
+ ((etch_object*)to)->is_static = ((etch_object*)from)->is_static;
+ to->content_type = from->content_type;
+ to->is_readonly = from->is_readonly;
+ to->freehook = from->freehook;
+}
+
+
+/* - - - - - - - - - -
+ * iteration lock
+ * - - - - - - - - - -
+ */
+
+/*
+ * etch_arraylist_getlock()
+ * explicitly set this list's synchronization lock, waiting if unavailable.
+ * this should be used only for locking a list prior to iterating the list.
+ * for synchronization of list operations, the presence of list.synchook
+ * and list.synclock is sufficient.
+ */
+int etch_arraylist_getlock (etch_arraylist* list)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ ETCH_ASSERT(list && ((etch_object*)list)->synclock);
+ if(((etch_object*)list)->synclock) {
+ status = etch_mutex_lock(((etch_object*)list)->synclock);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * etch_arraylist_trylock()
+ * explicitly set this list's synchronization lock, failing if unavailable.
+ * this should be used only for locking a list prior to iterating the list.
+ * for synchronization of list operations, the presence of list.synchook
+ * and list.synclock is sufficient.
+ */
+int etch_arraylist_trylock (etch_arraylist* list)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ ETCH_ASSERT(list && ((etch_object*)list)->synclock);
+ if(((etch_object*)list)->synclock) {
+ status = etch_mutex_trylock(((etch_object*)list)->synclock);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * etch_arraylist_rellock()
+ * release explicitly set this list's synchronization lock.
+ * this should be used only for unlocking a list after iterating the list.
+ * for synchronization of list operations, the presence of list.synchook
+ * and list.synclock is sufficient.
+ */
+int etch_arraylist_rellock (etch_arraylist* list)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ ETCH_ASSERT(list && ((etch_object*)list)->synclock);
+ if(((etch_object*)list)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)list)->synclock);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/* - - - - - - - - - -
+ * i_iterable
+ * - - - - - - - - - -
+ */
+
+/*
+ * etch_arraylist_iterable_first()
+ * i_iterable first() implementation
+ */
+int etch_arraylist_iterable_first(etch_iterator* iter)
+{
+ etch_arraylist* list = NULL;
+ if (!iter || !iter->collection) return -1;
+ list = iter->collection;
+ if (!list->base || !list->count) return -1;
+
+ iter->current_value = etch_arraylist_get(list, 0);
+ iter->ordinal = iter->current_value? 1: 0;
+ return iter->ordinal? 0: -1;
+}
+
+
+/*
+ * etch_arraylist_iterable_next()
+ * i_iterable next() implementation
+ * functions as first() if there is no current position.
+ */
+int etch_arraylist_iterable_next(etch_iterator* iter)
+{
+ etch_arraylist* list = iter? iter->collection: NULL;
+ const int count = list? list->count: 0;
+ if (!count || !iter->ordinal) return -1;
+
+ iter->current_value = etch_arraylist_get(list, iter->ordinal);
+ iter->ordinal = iter->current_value? ++iter->ordinal: 0;
+ return iter->ordinal? 0: -1;
+}
+
+
+/*
+ * etch_arraylist_iterable_has_next()
+ * i_iterable has_next() implementation.
+ */
+int etch_arraylist_iterable_has_next(etch_iterator* iter)
+{
+ etch_arraylist* list = iter? iter->collection: NULL;
+ const int count = list? list->count: 0;
+ return count && iter->ordinal && (iter->ordinal <= count);
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_cache.c b/binding-c/runtime/c/src/main/common/etch_cache.c
new file mode 100644
index 0000000..017fcfc
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_cache.c
@@ -0,0 +1,429 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_cache.h -- etch cache methods.
+ */
+
+#include "etch_cache.h"
+#include "etch_hash.h"
+
+#define ETCH_CACHE_USES_ALPHA_KEY
+#define ETCH_CACHE_DEBUG FALSE
+#define ETCH_CACHE_INITIAL_SIZE 32
+#define ETCH_CACHE_KEYBUFLEN 20
+
+etch_cache_t* g_etch_cache = NULL;
+etch_mutex* g_etch_cache_mutex = NULL;
+
+/*
+ * ETCH_CACHE_USES_ALPHA_KEY switches on alpha cache keys rather than the normal
+ * 4-byte integer. experiencing a problem with either the hfirst or hnext
+ * hashtable functionality, this was an attempt to determine if the 4-byte keys
+ * were causing the problem. it appears not, so we can probably use int keys again.
+ */
+#ifdef ETCH_CACHE_USES_ALPHA_KEY
+
+char ckey[ETCH_CACHE_KEYBUFLEN];
+
+/*
+ * make_cache_key()
+ * given 32-bit key constructs alpha key for the cache returning key byte length
+ */
+static int etch_make_cache_key(const unsigned int ikey)
+{
+ char* pkey = ckey;
+ sprintf(pkey,"%d", ikey);
+ return (int)strlen(ckey);
+}
+
+/*
+ * etch_cache_populate_out()
+ * populate caller's out struct
+ */
+static void etch_cache_populate_out(etch_hashitem* useritem, etch_hashitem* curritem)
+{
+ useritem->key = curritem->key;
+ useritem->value = curritem->value;
+ useritem->hash = curritem->hash;
+}
+
+/*
+ * cache_findxl()
+ */
+void* etch_cache_findxl(char* key, unsigned keylen, void** out)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+ int result = jenkins_find
+ (g_etch_cache->realtable, key, keylen, 0, (void**)&founditem);
+
+ #if ETCH_CACHE_DEBUG
+ #pragma warning(disable:4313)
+ if (result == 0)
+ printf("cache_found key %s len %d addr %08x\n",
+ key, keylen, founditem->value);
+ else printf("cache_notfound key %s len %d\n", key, keylen);
+ #endif
+
+ if (result == -1) return NULL;
+
+ if (out)
+ etch_cache_populate_out(*out, founditem);
+
+ return founditem->value;
+}
+
+
+/*
+ * cache_find()
+ * locate cached object with specified key, returning it or NULL
+ */
+void* etch_cache_find(const unsigned int objtype, void** out)
+{
+ unsigned keylen = 0;
+ void* res = NULL;
+ etch_mutex_lock(g_etch_cache_mutex);
+ keylen = etch_make_cache_key(objtype);
+ res = etch_cache_findxl(ckey, keylen, out);
+ etch_mutex_unlock(g_etch_cache_mutex);
+ return res;
+}
+
+
+/*
+ * cache_findx()
+ * locate cached object with specified ansi char key, returning it or NULL
+ */
+void* etch_cache_findx(char* key, void** out)
+{
+ unsigned keylen = (unsigned)strlen(key);
+
+ return etch_cache_findxl(key, keylen, out);
+}
+
+
+/*
+ * cache_find_by_hash()
+ * locate cached object with specified hashkey, returning it or NULL
+ */
+void* etch_cache_find_by_hash(const unsigned hash, void** out)
+{
+ int result;
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+ result = jenkins_findh(g_etch_cache->realtable, hash, 0, (void**)&founditem);
+
+ if (result == -1) return NULL;
+
+ if (out)
+ etch_cache_populate_out(*out, founditem);
+
+ return founditem->value;
+}
+
+
+/*
+ * cache_current()
+ * return cached object at current position
+ */
+etch_hashitem* etch_cache_current()
+{
+ int result;
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+ result = jenkins_current(g_etch_cache->realtable, 0, (void**)&founditem);
+
+ return (result == -1)? NULL: founditem;
+}
+
+
+/*
+ * cache_delxl()
+ * Remove specified object from cache given ansi char key and length.
+ * Return pointer to cached object, which is not freed here.
+ */
+void* etch_cache_delxl(char* key, const unsigned keylen)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+ int result = jenkins_remove
+ (g_etch_cache->realtable, ckey, keylen, 0, (void**)&founditem);
+
+ if (result == -1) return NULL;
+ free(founditem->key); /* free 4-byte key allocated in cache_add() */
+ return founditem->value;
+}
+
+
+/*
+ * cache_del()
+ * Remove specified object from cache given integer key.
+ * Return pointer to cached object, which is not freed here.
+ */
+void* etch_cache_del(const unsigned int objtype)
+{
+ unsigned keylen = 0;
+ void* res = NULL;
+
+ etch_mutex_lock(g_etch_cache_mutex);
+ keylen = etch_make_cache_key(objtype);
+ res = etch_cache_delxl(ckey, keylen);
+ etch_mutex_unlock(g_etch_cache_mutex);
+ return res;
+}
+
+
+/*
+ * cache_delx()
+ * Remove specified object from cache given ansi char key.
+ * Return pointer to cached object, which is not freed here.
+ */
+void* etch_cache_delx (char* key)
+{
+ unsigned keylen = (unsigned)strlen(key);
+
+ return etch_cache_delxl(key, keylen);
+}
+
+
+/*
+ * cache_insertxl()
+ * Add specified object to cache given ansi char key and char length,
+ * with existence test optional.
+ * Return hash of supplied key, or zero.
+ */
+int etch_cache_insertxl (char* key, const unsigned keylen, void* data, const int is_check)
+{
+ int result = 0, keylent = 0;
+ char* pkey = NULL;
+ void* pval = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* inserteditem = &hashbucket;
+ memset(&hashbucket, 0, sizeof(etch_hashitem));
+
+ if (is_check)
+ { pval = etch_cache_findxl(key, keylen, (void**)&inserteditem);
+ if (pval) return inserteditem->hash; /* entry exists */
+ }
+
+ keylent = keylen + 1;/* add new entry */
+ pkey = etch_malloc(keylent, 0);
+ strcpy(pkey, key);
+
+ #if CACHE_DEBUG
+ #pragma warning(disable:4313)
+ printf("cache_insertxl key %s len %d addr %08x\n", pkey, keylen, data);
+ #endif
+
+ result = jenkins_insert
+ (g_etch_cache->realtable, pkey, keylen, data, 0, 0, (void**)&inserteditem);
+
+ /* cache_dump(); */
+
+ return inserteditem->hash;
+}
+
+
+/*
+ * cache_insert()
+ * Add specified object to cache with existence test optional.
+ * Return inserted item hash, or zero.
+ */
+int etch_cache_insert (const unsigned int key, void* data, const int is_check)
+{
+ int keylen = 0;
+ int res = 0;
+ etch_mutex_lock(g_etch_cache_mutex);
+ keylen = etch_make_cache_key(key);
+ res = etch_cache_insertxl(ckey, keylen, data, is_check);
+ etch_mutex_unlock(g_etch_cache_mutex);
+ return res;
+}
+
+
+/*
+ * cache_insertx()
+ * Add specified object to cache with existence test optional.
+ * Return inserted item hash, or zero.
+ */
+int etch_cache_insertx (char* key, void* data, const int is_check)
+{
+ unsigned keylen = (unsigned)strlen(key);
+
+ return etch_cache_insertxl(key, keylen, data, is_check);
+}
+
+
+/*
+ * cache_add()
+ * Add specified object to cache given integer key.
+ * Return 0 or -1.
+ */
+int etch_cache_add(const unsigned int objtype, void* data)
+{
+ return etch_cache_insert (objtype, data, TRUE);
+}
+
+
+/*
+ * cache_addx()
+ * Add specified object to cache.
+ * Return 0 or -1.
+ */
+int etch_cache_addx(char* key, void* data)
+{
+ return etch_cache_insertx(key, data, TRUE);
+}
+
+
+#else // #ifdef ETCH_CACHE_USES_ALPHA_KEY
+
+
+/*
+ * cache_find()
+ * locate cached object with specified key, returning it or NULL
+ */
+void* etch_cache_find(const unsigned int objtype)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+ int result = jenkins_find
+ (g_etch_cache->realtable, (char*)&objtype, sizeof(int), 0, &founditem);
+ return result == -1? NULL: founditem->value;
+}
+
+
+/*
+ * cache_del()
+ * Remove specified object from cache.
+ * Return pointer to cached object, which is not freed here.
+ */
+void* etch_cache_del (const unsigned int objtype)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+ int result = jenkins_remove
+ (g_etch_cache->realtable, (char*)&objtype, sizeof(int), 0, &founditem);
+ if (result == -1) return NULL;
+ free(founditem->key); /* free 4-byte key allocated in cache_add() */
+ return founditem->value;
+}
+
+
+*
+ * cache_add()
+ * Add specified object to cache.
+ * Return pointer to item's data entry, which is not freed here.
+ */
+int etch_cache_add (const unsigned int objtype, void* data)
+{
+ int result = 0;
+ char* pkey = 0;
+ void* pval = cache_find(objtype);
+ if (pval) return 0; /* entry exists */
+
+ pkey = etch_malloc(sizeof(int), 0); /* add new entry */
+ memcpy(pkey, &objtype, sizeof(int));
+
+ result = jenkins_insert
+ (g_etch_cache->realtable, pkey, sizeof(int), data, 0, 0, 0);
+ return result;
+}
+
+
+#endif // #ifdef ETCH_CACHE_USES_ALPHA_KEY
+
+
+int etch_cache_freehook(void* key, void* value)
+{
+ etch_free(key);
+ etch_object_destroy((vtabmask*)value);
+
+ return 1;
+}
+
+/*
+ * cache_create()
+ * create the global cache as an untracked hashtable
+ */
+etch_status_t etch_cache_create()
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ assert(g_etch_cache == NULL);
+
+ etch_status = etch_mutex_create(&g_etch_cache_mutex, ETCH_MUTEX_NESTED, NULL);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ return etch_status;
+ }
+ g_etch_cache = new_systemhashtable(ETCH_CACHE_INITIAL_SIZE);
+ g_etch_cache->freehook = etch_cache_freehook;
+
+ return etch_status;
+}
+
+
+etch_status_t etch_cache_set_freehook(etch_cache_freehook_func freehook)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ g_etch_cache->freehook = freehook;
+ return rv;
+}
+
+
+/*
+ * cache_clear()
+ * remove all objects from the runtime list, freeing both key memory and
+ * data object memory for each. Returns the count of buckets so cleared.
+ */
+int etch_cache_clear()
+{
+ int result = jenkins_clear(g_etch_cache->realtable, TRUE, TRUE, g_etch_cache, 0);
+ return result;
+}
+
+/*
+ * cache_destroy()
+ * clear the cache and destroy the cache object
+ */
+int etch_cache_destroy()
+{
+ int result = etch_cache_clear();
+ result = jenkins_destroy(g_etch_cache->realtable, 0, 0);
+ etch_free(g_etch_cache);
+ g_etch_cache = NULL;
+ etch_object_destroy(g_etch_cache_mutex);
+ g_etch_cache_mutex = NULL;
+ return result;
+}
+
+/*
+ * cache_count()
+ * return number of items in the runtime cache
+ */
+int etch_cache_count()
+{
+ return jenkins_count(g_etch_cache->realtable, 0, 0);
+}
+
diff --git a/binding-c/runtime/c/src/main/common/etch_collection.c b/binding-c/runtime/c/src/main/common/etch_collection.c
new file mode 100644
index 0000000..63b8445
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_collection.c
@@ -0,0 +1,188 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_collection.c -- collection support
+ */
+
+#include "etch_collection.h"
+#include "etch_object.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+
+int default_iterable_first (etch_iterator*);
+int default_iterable_next (etch_iterator*);
+int default_iterable_has_next(etch_iterator*);
+
+
+/**
+ * new_iterable()
+ * constructor for an i_iterable interface
+ */
+i_iterable* new_iterable(i_iterable* thisp, i_iterable* parent, iterable_first func_first,
+ iterable_next func_next, iterable_has_next func_hasnext)
+{
+ i_iterable* vtab = NULL;
+
+ if (thisp) /* initializing existing object? */
+ vtab = thisp;
+ else vtab = new_vtable(parent, sizeof(i_iterable), CLASSID_ITERABLE_VTAB);
+
+ if (NULL == vtab) return NULL;
+
+ vtab->first = func_first? func_first: default_iterable_first;
+ vtab->next = func_next? func_next: default_iterable_next;
+ vtab->has_next = func_hasnext? func_hasnext: default_iterable_has_next;
+
+ return vtab;
+}
+
+
+/**
+ * destroy_iterator()
+ * iterator destructor
+ */
+int destroy_iterator(void* data)
+{
+ etch_iterator* iterator = data;
+
+ /* iterator owns itself, and possibly the collection.
+ * its vtable is owned by the collection. */
+ if (!is_etchobj_static_shell(iterator))
+ {
+ if (iterator->is_own_collection)
+ ((etch_object*)iterator->collection)->destroy(iterator->collection);
+
+ etch_free(iterator);
+ }
+
+ return 0;
+}
+
+
+/**
+ * clone_iterator()
+ * iterator copy constructor
+ */
+void* clone_iterator(void* data)
+{
+ etch_iterator* iterator = data;
+ etch_iterator* newobj = etch_malloc(sizeof(struct etch_iterator), ETCHTYPEB_ITERATOR);
+ memcpy(newobj, iterator, sizeof(struct etch_iterator));
+ return newobj;
+}
+
+
+/**
+ * new_iterator()
+ * constructor for an etch_iterator object.
+ */
+etch_iterator* new_iterator(void* collection, i_iterable* iterable)
+{
+ etch_iterator* iterator = etch_malloc(sizeof(etch_iterator), ETCHTYPEB_ITERATOR);
+
+ set_iterator(iterator, collection, iterable);
+
+ return iterator;
+}
+
+
+/**
+ * set_iterator()
+ * constructor for an existing etch_iterator object.
+ */
+int set_iterator(etch_iterator* iterator, void* collection, i_iterable* iterable)
+{
+ memset(iterator, 0, sizeof(etch_iterator));
+
+ ((etch_object*)iterator)->obj_type = ETCHTYPEB_ITERATOR;
+ ((etch_object*)iterator)->class_id = CLASSID_ITERATOR;
+
+ ((etch_object*)iterator)->vtab = (vtabmask*)iterable;
+ iterator->collection = collection;
+
+ ((etch_object*)iterator)->destroy = destroy_iterator;
+ ((etch_object*)iterator)->clone = clone_iterator;
+
+ iterator->first = iterable->first;
+ iterator->next = iterable->next;
+ iterator->has_next = iterable->has_next;
+
+ iterator->first(iterator); /* establish initial position */
+ return 0;
+}
+
+
+/**
+ * new_empty_iterator()
+ * constructor for an empty etch_iterator object, i.e. has_next() is false
+ */
+etch_iterator* new_empty_iterator()
+{
+ etch_iterator* iterator = etch_malloc(sizeof(etch_iterator), ETCHTYPEB_ITERATOR);
+
+ ((etch_object*)iterator)->obj_type = ETCHTYPEB_ITERATOR;
+ ((etch_object*)iterator)->class_id = CLASSID_ITERATOR;
+ ((etch_object*)iterator)->destroy = destroy_iterator;
+
+ iterator->first = default_iterable_first;
+ iterator->next = default_iterable_next;
+ iterator->has_next = default_iterable_has_next;
+
+ return iterator;
+}
+
+
+/**
+ * default_iterable_first()
+ * i_iterable first() default virtual method
+ */
+int default_iterable_first(etch_iterator* i)
+{
+ return -1;
+}
+
+
+/**
+ * default_iterable_next()
+ * i_iterable next() default virtual method
+ */
+int default_iterable_next(etch_iterator* i)
+{
+ return -1;
+}
+
+
+/**
+ * default_iterable_has_next()
+ * i_iterable has_next() default virtual method
+ */
+int default_iterable_has_next (etch_iterator* i)
+{
+ return FALSE;
+}
+
+
+/**
+ * etch_comparator_noteq(), etch_comparator_equal
+ * comparators to use when a result not dependent on parameters is desired,
+ * such as when using an add_from method to add to a collection when the
+ * target collection is empty and thus no contains() comparison is needed.
+ */
+int etch_comparator_noteq(void* a, void* b) { return -1;}
+int etch_comparator_equal(void* a, void* b) { return 0; }
diff --git a/binding-c/runtime/c/src/main/common/etch_config.c b/binding-c/runtime/c/src/main/common/etch_config.c
new file mode 100644
index 0000000..995f1a0
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_config.c
@@ -0,0 +1,322 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_config.c
+ * config items and config file parse.
+ * config file is formatted as a java-style properties file expected as ansi or utf-8.
+ */
+
+#include "etch.h"
+#include "etch_config.h"
+#include "etch_general.h"
+
+#define ETCH_CONFIG_LINE_MAX 1024
+
+/*
+static const char* ETCH_CONFIG_CATEGORY = "etch_config";
+*/
+struct etch_config_t
+{
+ struct etch_config_entry* data;
+ uint16 length;
+ uint16 size;
+};
+
+struct etch_config_entry
+{
+ char* name;
+ char* value;
+};
+
+etch_status_t etch_config_create(etch_config_t** config)
+{
+ etch_config_t* newconfig = NULL;
+
+ if(config == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ newconfig = etch_malloc(sizeof(etch_config_t), 0);
+ if(newconfig == NULL) {
+ return ETCH_ENOMEM;
+ }
+ memset(newconfig, 0, sizeof(etch_config_t));
+ *config = newconfig;
+
+ // default properties
+ etch_config_set_property(*config, "etch.validate.write", "1");
+ etch_config_set_property(*config, "etch.validate.read", "1");
+ etch_config_set_property(*config, "etch.mailbox.timeout.read", "3000");
+ etch_config_set_property(*config, "etch.mailbox.timeout.write", "3000");
+ // destroy message with mailbox
+ etch_config_set_property(*config, "etch.mailbox.destroy.message", "1");
+ // TODO: add default values
+ etch_config_set_property(*config, "etch.log", "error");
+
+ etch_config_set_property(*config, "etch.log.consoleappender", "consoleappender");
+
+ etch_config_set_property(*config, "etch.log.fileappender", "fileappender");
+ etch_config_set_property(*config, "etch.log.fileappender.file", "C:/dfdr.log");
+
+ etch_config_set_property(*config, "etch.maxconnections", "40");
+
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_config_open(struct etch_config_t* config, const char* filepath)
+{
+ FILE *file = NULL;
+ int c = 0;
+ char linebuf[ETCH_CONFIG_LINE_MAX];
+ int16 linepos = 0;
+ char lineend = 0;
+
+ if(config == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ file = fopen(filepath, "r");
+ if(file == NULL) {
+ return ETCH_EFILENOTFOUND;
+ }
+
+ while(c != EOF) {
+ char ch = 0;
+ c = fgetc(file);
+ ch = c;
+ // determine end of config line
+ if(c == EOF || c == '\n') {
+ ch = '\0';
+ if(linepos > 0 && linebuf[linepos - 1] == '\r') {
+ linepos--;
+ }
+ lineend = 1;
+ }
+ if(linepos >= ETCH_CONFIG_LINE_MAX - 1) {
+ // error
+ lineend = 0;
+ linepos = 0;
+ continue;
+ }
+
+ linebuf[linepos++] = ch;
+ if(lineend) {
+ char *name = NULL;
+ char *value = NULL;
+ char *line = strtrim(linebuf);
+
+ // check for comments or empty lines
+ if(line[0] != '#' && line[0] != '\0') {
+ // parse line
+ name = strtok(line, "=");
+ name = strtrim(name);
+ if(name != NULL) {
+ value = strtok(NULL, "=");
+ value = strtrim(value);
+ }
+ etch_config_set_property(config, name, value);
+ }
+ lineend = 0;
+ linepos = 0;
+ }
+ }
+
+ fclose(file);
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_config_set_property(etch_config_t* config, const char* name, const char* value)
+{
+ int16 i = 0;
+ int16 index = -1;
+
+ if(config == NULL) {
+ return ETCH_EINVAL;
+ }
+ if(name == NULL || value == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ // check if resize is neccesary
+ if(config->length == config->size) {
+ uint16 newsize = config->size + 10;
+ struct etch_config_entry* temp = etch_malloc(sizeof(struct etch_config_entry) * newsize, 0);
+ memset(temp, 0, sizeof(struct etch_config_entry) * newsize);
+ if(config->size > 0) {
+ memcpy(temp, config->data, sizeof(struct etch_config_entry) * config->size);
+ etch_free(config->data);
+ }
+ config->data = temp;
+ config->size = newsize;
+ }
+
+ // check if key exists
+ for(i = 0; i < config->length; i++) {
+ if(strcmp(config->data[i].name, name) == 0) {
+ index = i;
+ break;
+ }
+ }
+
+ // if index == -1 append new entry
+ if(index == -1) {
+ // write key
+ config->data[config->length].name = etch_malloc(strlen(name) + 1, 0);
+ memset(config->data[config->length].name, 0, (size_t)(strlen(name) + 1));
+ strcpy(config->data[config->length].name, name);
+ index = config->length++;
+ } else {
+ etch_free(config->data[index].value);
+ config->data[index].value = NULL;
+ }
+
+ // write value
+ config->data[index].value = etch_malloc(strlen(value) + 1, 0);
+ memset(config->data[index].value, 0, (size_t)(strlen(value) + 1));
+ strcpy(config->data[index].value, value);
+
+ return ETCH_SUCCESS;
+}
+
+int etch_config_has_property(struct etch_config_t* config, const char* name)
+{
+ int16 i = 0;
+
+ if(config == NULL || name == NULL) {
+ return 0;
+ }
+
+ // check if key exists
+ for(i = 0; i < config->length; i++) {
+ if(strcmp(config->data[i].name, name) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+etch_status_t etch_config_get_property_string(struct etch_config_t* config, const char* name, char** value)
+{
+ int16 i = 0;
+ int16 index = -1;
+
+ if(config == NULL || name == NULL|| value == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ // check if key exists
+ for(i = 0; i < config->length; i++) {
+ if(strcmp(config->data[i].name, name) == 0) {
+ index = i;
+ break;
+ }
+ }
+
+ if(index != -1) {
+ *value = config->data[i].value;
+ } else {
+ *value = NULL;
+ }
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_config_get_property_int(struct etch_config_t* config, const char* name, int32* value)
+{
+ int16 i = 0;
+ int16 index = -1;
+
+ if(config == NULL || name == NULL|| value == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ // check if key exists
+ for(i = 0; i < config->length; i++) {
+ if(strcmp(config->data[i].name, name) == 0) {
+ index = i;
+ break;
+ }
+ }
+
+ if(index != -1) {
+ *value = atoi(config->data[i].value);
+ } else {
+ *value = 0;
+ }
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_config_get_property_by_index(struct etch_config_t* config, uint16 index, char** value)
+{
+ if(config == NULL || value == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ if(index < config->length) {
+ *value = config->data[index].value;
+ } else {
+ *value = NULL;
+ }
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_config_clear(etch_config_t* config)
+{
+ int16 i = 0;
+
+ if(config == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ // check if key exists
+ for(i = 0; i < config->length; i++) {
+ etch_free(config->data[i].name);
+ etch_free(config->data[i].value);
+ }
+ config->length = 0;
+
+
+ return ETCH_SUCCESS;
+}
+
+uint16 etch_config_get_length(struct etch_config_t* config)
+{
+ if(config == NULL) {
+ return 0;
+ }
+ return config->length;
+}
+
+uint16 etch_config_get_size(struct etch_config_t* config)
+{
+ if(config == NULL) {
+ return 0;
+ }
+ return config->size;
+}
+
+etch_status_t etch_config_destroy(etch_config_t* config)
+{
+ if(config == NULL) {
+ return ETCH_EINVAL;
+ }
+ etch_config_clear(config);
+ etch_free(config->data);
+ etch_free(config);
+ return ETCH_SUCCESS;
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_encoding.c b/binding-c/runtime/c/src/main/common/etch_encoding.c
new file mode 100644
index 0000000..dcf8fbb
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_encoding.c
@@ -0,0 +1,231 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_encoding.c -- character encoding
+ */
+
+#include <apr_iconv.h>
+#include "etch_encoding.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+#include <wchar.h>
+
+static const char* LOG_CATEGORY = "etch_encoding";
+
+extern apr_pool_t* g_etch_main_pool;
+extern apr_thread_mutex_t* g_etch_main_pool_mutex;
+
+static const unsigned char CODEPAGE_TABLE_SIZE = 6;
+
+
+static apr_iconv_t* codepage_table = NULL;
+
+etch_status_t etch_encoding_initialize()
+{
+ size_t s = CODEPAGE_TABLE_SIZE * CODEPAGE_TABLE_SIZE * sizeof(apr_iconv_t);
+ codepage_table = etch_malloc(s,0);
+ memset(codepage_table, 0, s);
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_encoding_shutdown()
+{
+ int i = 0;
+
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ for(i = 0; i < CODEPAGE_TABLE_SIZE * CODEPAGE_TABLE_SIZE; i++) {
+ if(codepage_table[i] != NULL) {
+ apr_iconv_close(codepage_table[i], g_etch_main_pool);
+ }
+ }
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+ etch_free(codepage_table);
+ codepage_table = NULL;
+ return ETCH_SUCCESS;
+}
+
+
+static char* etch_encoding_get_encoding(unsigned char encoding)
+{
+ switch (encoding) {
+ case ETCH_ENCODING_ASCII:
+ return "iso-8859-1";
+ case ETCH_ENCODING_UTF8:
+ return "utf-8";
+ case ETCH_ENCODING_UCS2:
+ return "ucs2-internal";
+ case ETCH_ENCODING_UCS4:
+ return "ucs4-internal";
+ case ETCH_ENCODING_UTF16:
+ return "utf-16";
+ default:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "unsupported src-encoding %d\n", encoding);
+ ETCH_ASSERT(!"encoding not supported");
+ return 0;
+ }
+}
+
+
+etch_status_t etch_encoding_get_codepage(unsigned char src, unsigned char dst, apr_iconv_t* codepage)
+{
+ apr_iconv_t* temp = NULL;
+ apr_status_t status = APR_SUCCESS;
+ char* apr_codepage_src = NULL;
+ char* apr_codepage_dst = NULL;
+
+ apr_codepage_src = etch_encoding_get_encoding(src);
+ apr_codepage_dst = etch_encoding_get_encoding(dst);
+ if (apr_codepage_src == NULL || apr_codepage_dst == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ temp = &codepage_table[(CODEPAGE_TABLE_SIZE * src) + dst];
+ if (*temp == NULL) {
+ status = apr_iconv_open(apr_codepage_dst, apr_codepage_src, g_etch_main_pool, temp);
+ }
+ *codepage = *temp;
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+
+ if(status != APR_SUCCESS){
+ return ETCH_ERROR;
+ }
+ return ETCH_SUCCESS;
+}
+
+
+int etch_encoding_transcode(char** out, unsigned char outEncoding, const char* in, unsigned char inEncoding, unsigned int inByteCount, int* resultingByteCount, etch_pool_t* pool)
+{
+ // in
+ const char* apr_in_buf = in;
+ apr_size_t apr_in_bytecount = inByteCount;
+
+ // out
+ char* apr_out_buf = NULL;
+ apr_size_t apr_out_bytecount = 0;
+ apr_size_t apr_out_bytecount_original;
+
+ // translated
+ apr_size_t apr_translated = 0;
+
+ // converter
+ apr_iconv_t apr_cd = NULL;
+
+ apr_status_t apr_status = 0;
+ etch_status_t etch_status;
+
+ // chech input params
+ if (NULL == out) {
+ return -1;
+ }
+
+ if(pool == NULL) {
+ pool = g_etch_main_pool;
+ }
+
+ apr_out_bytecount = apr_in_bytecount*4; // worst case ascii->ucs4
+ apr_out_bytecount_original = apr_out_bytecount;
+ apr_out_buf = etch_malloc(apr_out_bytecount, ETCHTYPEB_BYTES);
+ (*out) = apr_out_buf;
+ memset(apr_out_buf, 0, apr_out_bytecount);
+
+ if(inEncoding == outEncoding){
+ memcpy(apr_out_buf,in,inByteCount);
+ return 0;
+ }
+
+ // open iconv
+ etch_status = etch_encoding_get_codepage(inEncoding, outEncoding, &apr_cd);
+
+ if(etch_status != ETCH_SUCCESS)
+ {
+ char buffer[512];
+ apr_strerror(apr_status, buffer, 512);
+ printf("%s", buffer);
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "encoding conversion error %d\n", apr_status);
+ return -1;
+ }
+
+ // convert
+ apr_status = apr_iconv(apr_cd, &apr_in_buf, &apr_in_bytecount, &apr_out_buf, &apr_out_bytecount, &apr_translated);
+ if(apr_status != APR_SUCCESS)
+ {
+ char buffer[512];
+ apr_strerror(apr_status, buffer, 512);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "encoding conversion error-code: %d error-msg: %s\n", apr_status, buffer);
+
+ //clean up
+ etch_free((*out));
+ (*out) = NULL;
+ return -1;
+ }
+ *resultingByteCount = (int)(apr_out_bytecount_original - apr_out_bytecount);
+
+ return 0;
+}
+
+unsigned char etch_encoding_for_wchar()
+{
+ switch (sizeof(wchar_t)) {
+ case 2:
+ return ETCH_ENCODING_UCS2;
+ case 4:
+ return ETCH_ENCODING_UCS4;
+ default:
+ ETCH_ASSERT(!"unknown wchar_t size");
+ return 0;
+ }
+}
+
+int etch_encoding_transcode_wchar(char** out, unsigned char outEncoding, const wchar_t* in, etch_pool_t* pool)
+{
+ int outByteCount;
+ unsigned int charcount;
+ unsigned char srcEncoding;
+ charcount = (unsigned int)wcslen(in);
+ srcEncoding = etch_encoding_for_wchar();
+ return etch_encoding_transcode(out, outEncoding, (const char*)in, srcEncoding, charcount * sizeof(wchar_t), &outByteCount, pool);
+}
+
+int etch_encoding_transcode_to_wchar(wchar_t** out, const void* in, unsigned char inEncoding, unsigned int inByteCount, etch_pool_t* pool)
+{
+ int outByteCount;
+ unsigned char dstEncoding;
+ dstEncoding = etch_encoding_for_wchar();
+ return etch_encoding_transcode((char**)out, dstEncoding, in, inEncoding, inByteCount, &outByteCount, pool);
+}
+
+unsigned int etch_encoding_get_sizeof_terminator(unsigned char encoding)
+{
+ switch (encoding) {
+ case ETCH_ENCODING_ASCII:
+ case ETCH_ENCODING_UTF8:
+ return 1;
+ case ETCH_ENCODING_UCS2:
+ return 2;
+ case ETCH_ENCODING_UCS4:
+ return 4;
+ case ETCH_ENCODING_UTF16:
+ case ETCH_ENCODING_UTF32:
+ default:
+ ETCH_ASSERT(!"encoding not supported");
+ return 0;
+ }
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_exception.c b/binding-c/runtime/c/src/main/common/etch_exception.c
new file mode 100644
index 0000000..5cad320
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_exception.c
@@ -0,0 +1,167 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_excp.c -- exception objects native and boxed
+ */
+
+#include "etch_encoding.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+static const char* LOG_CATEGORY = "etch_exception";
+/* builtin exception text */
+const wchar_t* excptext_excp_builtin = L"etch builtin exception";
+const wchar_t* excptext_excp_user = L"etch user defined exception";
+
+/**
+ * etch_exception
+ */
+struct etch_exception
+{
+ etch_object object;
+
+ etch_string* message;
+ uint32 errorcode;
+ excptype_t excptype;
+
+};
+
+/*
+ * default_excptext()
+ * find and return default text for exception type
+ */
+etch_string* default_excptext(const excptype_t type)
+{
+ wchar_t* text = NULL;
+ etch_string* res = NULL;
+ switch(type)
+ {
+ case EXCPTYPE_BUILTIN: text = (wchar_t*) excptext_excp_builtin; break;
+ case EXCPTYPE_USERDEFINED: text = (wchar_t*) excptext_excp_user; break;
+ default:
+ text = (wchar_t*) L"unknown error";
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_WARN, "no default exception text for type %d", type);
+ break;
+ }
+ res = new_stringw(text);
+ return res;
+}
+
+
+/**
+ * destroy_etch_exception()
+ */
+int destroy_etch_exception(void* p)
+{
+ etch_exception* ee = (etch_exception*) p;
+
+ if ((! is_etchobj_static_content(ee)) && ee->message) {
+ etch_object_destroy(ee->message);
+ }
+
+ return destroy_objectex((etch_object*) ee);
+}
+
+
+/**
+ * clone_etch_exception()
+ */
+void* clone_etch_exception(void* data)
+{
+ etch_exception* ee = (etch_exception*)data;
+ etch_exception* newobj = NULL;
+ newobj = new_etch_exception (ee->excptype);
+ if(ee->message) {
+ newobj->message = (etch_string*)etch_object_clone_func(ee->message);
+ }else {
+ newobj->message = NULL;
+ }
+ newobj->errorcode = ee->errorcode;
+ return newobj;
+}
+
+/**
+ * new_etch_exception()
+ * constructor for wrapped exception
+ */
+etch_exception* new_etch_exception(const excptype_t type)
+{
+ etch_exception* newobj = (etch_exception*) new_object
+ (sizeof(struct etch_exception), ETCHTYPEB_EXCEPTION, CLASSID_EXCEPTION);
+
+ ((etch_object*)newobj)->destroy = destroy_etch_exception;
+ ((etch_object*)newobj)->clone = clone_etch_exception;
+ newobj->errorcode = 0;
+ newobj->excptype = type;
+ newobj->message = default_excptext(type);
+
+ return newobj;
+}
+
+/**
+ * create a new builtin etch exception with errorcode
+ */
+etch_exception* new_etch_exception_from_errorcode(int errorcode){
+ etch_exception* result = new_etch_exception(EXCPTYPE_BUILTIN);
+ result->errorcode = errorcode;
+ return result;
+}
+
+void etch_exception_set_message(etch_exception* ex, etch_string* mess)
+{
+ if(ex->message){
+ etch_object_destroy(ex->message);
+ }
+ ex->message = mess;
+}
+
+uint32 etch_exception_get_errorcode(etch_exception* ex) {
+ if(ex){
+ return ex->errorcode;
+ }
+ return 0;
+}
+
+etch_string* etch_exception_get_message(etch_exception* ex)
+{
+ return ex->message;
+}
+
+etch_status_t etch_exception_get_type(etch_exception* exception, excptype_t* type)
+{
+ if(exception == NULL || type == NULL) {
+ return ETCH_EINVAL;
+ }
+ *type = exception->excptype;
+ return ETCH_SUCCESS;
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binding-c/runtime/c/src/main/common/etch_flexbuffer.c b/binding-c/runtime/c/src/main/common/etch_flexbuffer.c
new file mode 100644
index 0000000..aa16f29
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_flexbuffer.c
@@ -0,0 +1,722 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etchflexbuf.c
+ * flex buffer
+ */
+
+#include "etch_flexbuffer.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+#define ETCH_INIT_FLEXBUFSIZE 2048
+#define ETCH_MAX_FLEXBUFSIZE (4*1024*1024)
+
+static void etch_flexbuf_fix_length (etch_flexbuffer*);
+static int etch_flexbuf_ensure_size (etch_flexbuffer*, size_t);
+
+/*
+ * java binding syntax is confusing and ambiguous so we change it a bit. java
+ * calls the data region in the buffer "length". since "length" is non-specific,
+ * and there are other "length"s, such as "buffer.length", and "object.length",
+ * we'll call it "datalen", to mean "meaningful bytes within the buffer". java
+ * also uses "buffer.length", which is the size of the allocated buffer. c of
+ * course does not associate properties with arrays, so we carry an additional
+ * property "bufsize" to indicate the allocated length of the byte array.
+ */
+
+
+/**
+ * new_flexbuffer()
+ * etch_flexbuffer constructor
+ */
+etch_flexbuffer *new_flexbuffer (size_t bufsize)
+{
+ void *buf;
+ if (bufsize <= 0 || bufsize > ETCH_MAX_FLEXBUFSIZE) bufsize = ETCH_INIT_FLEXBUFSIZE;
+
+ buf = etch_malloc(bufsize, ETCHTYPEB_BYTES);
+ memset(buf, 0, bufsize);
+
+ return etch_flexbuf_create_bi (buf, bufsize, 0, 0);
+}
+
+
+/**
+ * new_flexwriter_from()
+ * etch_flexbuffer constructor with index set to write
+ */
+etch_flexbuffer *new_flexwriter_from (void *buf, size_t datalen, size_t bufsize)
+{
+ return new_flexbuffer_from (buf, datalen, bufsize, datalen);
+}
+
+
+/**
+ * new_flexbuffer_from()
+ * etch_flexbuffer constructor.
+ * @param buf data which is to become the internal buffer. caller relinquishes this memory.
+ * @param datalen length of real data in bytes.
+ * @param bufsize size of buffer in bytes.
+ * @param buffer index at which to start.
+ */
+etch_flexbuffer *new_flexbuffer_from (void* buf, size_t datalen, size_t bufsize, size_t index)
+{
+ if (bufsize <= 0 || bufsize > ETCH_MAX_FLEXBUFSIZE) bufsize = ETCH_INIT_FLEXBUFSIZE;
+ if (datalen > bufsize || (!buf && datalen)) return NULL;
+ if (NULL == buf)
+ { buf = etch_malloc(bufsize, ETCHTYPEB_BYTES);
+ memset(buf, 0, bufsize);
+ }
+
+ return etch_flexbuf_create_bi (buf, bufsize, datalen, index);
+}
+
+
+/**
+ * etch_flexbuf_create_b()
+ * create a flex buffer out of an existing buffer, ready to read to,
+ * using specified size and an index of zero.
+ *
+ * @param buf the existing buffer.
+ * @param bufsize the buffer size.
+ * @param datalen the data length in the buffer
+ *
+ * @return the created and initialized the flex buffer.
+ *
+ */
+etch_flexbuffer *etch_flexbuf_create_b (void *buf, size_t bufsize, size_t datalen)
+{
+ return etch_flexbuf_create_bi(buf, bufsize, datalen, 0);
+}
+
+
+/**
+ * etch_flexbuf_create_bi()
+ * create a flex buffer out of an existing buffer, ready to read with specified
+ * index and size.
+ * @param buf the existing buffer.
+ * @param bufsize the buffer size.
+ * @param datalen the data length in the buffer
+ * @param index current position of the buffer.
+ */
+etch_flexbuffer *etch_flexbuf_create_bi(void *buf, size_t bufsize, size_t datalen, size_t index)
+{
+ etch_flexbuffer *fbuf = (etch_flexbuffer*) new_object(sizeof(etch_flexbuffer), ETCHTYPEB_FLEXBUF, CLASSID_FLEXBUF);
+
+ ((etch_object*)fbuf)->destroy = destroy_etch_flexbuffer;
+
+ fbuf->buf = buf;
+ fbuf->bufsize = bufsize;
+ fbuf->datalen = datalen;
+ fbuf->index = index;
+ fbuf->is_littleendian = IS_ETCH_TRANSPORT_LITTLEENDIAN;
+ return fbuf;
+}
+
+
+/**
+ * etch_flexbuf_get_buffer()
+ * @return the current byte array. might change if any operation
+ * needs to extend length past the end of the array.
+ */
+byte* etch_flexbuf_get_buffer (etch_flexbuffer *fbuf)
+{
+ return fbuf->buf;
+}
+
+
+/**
+ * etch_flexbuf_clear()
+ * zero fill the internal buffer
+ */
+void etch_flexbuf_clear (etch_flexbuffer *fbuf)
+{
+ memset (fbuf->buf, 0, fbuf->bufsize);
+}
+
+
+/**
+ * etch_flexbuf_ensure_size().
+ * verify sufficient buffer capacity, reallocating if necessary.
+ * @return boolean value indicating if buffer is as requested.
+ */
+static int etch_flexbuf_ensure_size (etch_flexbuffer *fbuf, size_t reqsize)
+{
+ byte *newbuf = NULL;
+ size_t newsize = fbuf->bufsize;
+ if (reqsize < ETCH_INIT_FLEXBUFSIZE)
+ reqsize = ETCH_INIT_FLEXBUFSIZE;
+ if (reqsize <= newsize) return TRUE;
+ if (reqsize > ETCH_MAX_FLEXBUFSIZE) return FALSE;
+
+ while(reqsize > newsize) newsize += ETCH_INIT_FLEXBUFSIZE;
+ newbuf = etch_realloc (fbuf->buf, newsize, 0);
+
+ fbuf->buf = newbuf;
+ fbuf->bufsize = newsize;
+ return TRUE;
+}
+
+
+/**
+ * destroy_etch_flexbuffer()
+ * etch_flexbuffer destructor
+ */
+int destroy_etch_flexbuffer(void* obj)
+{
+ etch_flexbuffer* fbuf = (etch_flexbuffer*)obj;
+ if (fbuf == NULL) return 0;
+
+ if (!is_etchobj_static_content(fbuf))
+ etch_free(fbuf->buf); /* OK if null */
+
+ destroy_objectex((etch_object*) fbuf);
+ return 0;
+}
+
+
+/**
+ * sets a new data length. If the index is larger than new length,
+ * the index is set to the new length as well.
+ * the method name was retained from java, however it is more properly
+ * set_data_length, since as noted previously, "length" is ambiguous,
+ * referring to data length, not to allocated bytes.
+ */
+int etch_flexbuf_set_length (etch_flexbuffer *fbuf, size_t new_datalen)
+{
+ int result = 0;
+
+ if (new_datalen >= 0 && etch_flexbuf_ensure_size(fbuf, new_datalen))
+ {
+ fbuf->datalen = new_datalen;
+
+ if (fbuf->index > new_datalen)
+ fbuf->index = new_datalen;
+ }
+ else result = -1;
+
+ return result;
+}
+
+
+/**
+ * sets a new buffer index. the index must be greater than 0 and less
+ * than the buffer size
+ * @param fbuf the buffer pointer to be set.
+ * @param index the new index for the buffer.
+ */
+int etch_flexbuf_set_index (etch_flexbuffer *fbuf, size_t index)
+{
+ if (index < 0 || index > fbuf->datalen) return -1;
+ fbuf->index = index;
+ return 0;
+}
+
+
+/**
+ * get the number of bytes available in the buffer.
+ * @return the available space in the buffer.
+ */
+size_t etch_flexbuf_avail (etch_flexbuffer *fbuf)
+{
+ return (fbuf->datalen - fbuf->index);
+}
+
+
+/**
+ * etch_flexbuffer_reset_to()
+ * set index to zero and length to specified length.
+ */
+int etch_flexbuffer_reset_to (etch_flexbuffer *fbuf, size_t new_datalen)
+{
+ const int result = etch_flexbuf_set_length (fbuf, new_datalen);
+ fbuf->index = 0;
+ return result;
+}
+
+
+/**
+ * etch_flexbuf_reset()
+ * set index and length to zero, same as etch_flexbuf_set_index(buf, 0)
+ */
+etch_flexbuffer *etch_flexbuf_reset (etch_flexbuffer *fbuf)
+{
+ fbuf->index = fbuf->datalen = 0;
+ return fbuf;
+}
+
+
+/**
+ * Compacts the buffer by moving remaining data (from index to length)
+ * to the front of the buffer. Sets index to 0, and sets length to
+ * avail (before index was changed).
+ * @return this flex buffer object.
+ */
+etch_flexbuffer *etch_flexbuf_compact(etch_flexbuffer *fbuf)
+{
+ size_t curlen;
+ if (fbuf->index == 0) return fbuf;
+
+ if (0 == (curlen = etch_flexbuf_avail(fbuf)))
+ {
+ etch_flexbuf_set_length(fbuf, 0);
+ return fbuf;
+ }
+
+ memmove(fbuf->buf, fbuf->buf+fbuf->index, curlen);
+ fbuf->index = 0;
+ fbuf->datalen = curlen;
+
+ return fbuf;
+}
+
+
+/**
+ * Copies data from the internal buffer to buf.
+ *
+ * @param fbuf the flex buffer to get data from.
+ * @param buf a buffer to receive the data. At most
+ * min( len, avail() ) bytes are transferred, starting
+ * at off.
+ * @param off the index in buf to receive the data.
+ * Off must be >= 0 && <= buf.datalen.
+ * @param len the max amount of data to transfer. Len
+ * must be >= 0 and <= buf.datalen - off.
+ * @return the amount of data transferred.
+ */
+size_t etch_flexbuf_get(etch_flexbuffer *fbuf, byte *buf, size_t off, size_t len)
+{
+ size_t bytecount = 0, avail = etch_flexbuf_avail(fbuf);
+ if (len <= 0 || NULL == buf) return 0;
+
+ bytecount = len < avail ? len : avail ;
+ /* changed 6/16 to add index */
+ memcpy(buf + off, fbuf->buf + fbuf->index + off, bytecount);
+ fbuf->index += bytecount;
+
+ return bytecount;
+}
+
+
+/**
+ * etch_flexbuf_get_allfrom()
+ * return buffer contents starting at index, in a byte vector, caller owns returned memory
+ */
+byte* etch_flexbuf_get_allfrom(etch_flexbuffer* fbuf, size_t index, size_t* out_count)
+{
+ byte* newbuf = 0;
+ size_t bytecount = 0;
+ if (index < 0) index = fbuf->index;
+ if (index > fbuf->datalen) index = fbuf->datalen;
+ fbuf->index = index; /* 6/16 so etch_flexbuf_get can skip index */
+
+ bytecount = fbuf->datalen - index;
+ newbuf = etch_malloc(bytecount, ETCHTYPEB_BYTES);
+
+ bytecount = etch_flexbuf_get(fbuf, newbuf, 0, bytecount);
+
+ if (out_count) *out_count = bytecount;
+ return newbuf;
+}
+
+
+/**
+ * etch_flexbuf_get_all()
+ * return buffer contents in a byte array, caller owns memory
+ */
+byte* etch_flexbuf_get_all(etch_flexbuffer* fbuf, size_t* out_count)
+{
+ return etch_flexbuf_get_allfrom(fbuf, fbuf->index, out_count);
+}
+
+
+/**
+ * return next byte in buffer
+ */
+int etch_flexbuf_get_byte(etch_flexbuffer *fbuf, byte* out)
+{
+ if (etch_flexbuf_avail(fbuf) < sizeof(byte)) return -1;
+ *out = fbuf->buf[fbuf->index++];
+ return 0;
+}
+
+/**
+ * etch_flexbuf_get_short()
+ * @return a short composed from the next 2 bytes.
+ */
+int etch_flexbuf_get_short(etch_flexbuffer *fbuf, short* out)
+{
+ int value, svalue;
+ if (etch_flexbuf_avail(fbuf) < sizeof(short)) return -1;
+
+ if (fbuf->is_littleendian)
+ {
+ value = fbuf->buf[fbuf->index++] & 255;
+ svalue = (short) (value + ((fbuf->buf[fbuf->index++] & 255) << 8));
+ }
+ else
+ { value = fbuf->buf[fbuf->index++]; /* big endian */
+ svalue = (short) ((value << 8) + (fbuf->buf[fbuf->index++] & 255));
+ }
+
+ *out = svalue;
+ return 0;
+}
+
+
+/**
+ * etch_flexbuf_get_int()
+ * @return an int composed from the next 4 bytes.
+ */
+int etch_flexbuf_get_int(etch_flexbuffer *fbuf, int32* out)
+{
+ int32 value = 0;
+ size_t bytes_avail = 0;
+
+ bytes_avail = etch_flexbuf_avail(fbuf);
+ if(bytes_avail < sizeof(int32))
+ {
+ return -1;
+ }
+
+ if(fbuf->is_littleendian)
+ {
+ value = value | fbuf->buf[fbuf->index+0] << 0;
+ value = value | fbuf->buf[fbuf->index+1] << 8;
+ value = value | fbuf->buf[fbuf->index+2] << 16;
+ value = value | fbuf->buf[fbuf->index+3] << 24;
+ fbuf->index += sizeof(int32);
+ }
+ else
+ {
+ value = value | fbuf->buf[fbuf->index+3] << 0;
+ value = value | fbuf->buf[fbuf->index+2] << 8;
+ value = value | fbuf->buf[fbuf->index+1] << 16;
+ value = value | fbuf->buf[fbuf->index+0] << 24;
+ fbuf->index += sizeof(int32);
+ }
+ *out = value;
+ return 0;
+}
+
+/**
+ * etch_flexbuf_get_long()
+ * @return a long composed from the next 8 bytes.
+ * note jim are we taking sign into consideration on reversal?
+ */
+int etch_flexbuf_get_long(etch_flexbuffer *fbuf, int64* out)
+{
+ int64 value = 0;
+ size_t bytes_avail = 0;
+
+ bytes_avail = etch_flexbuf_avail(fbuf);
+ if(bytes_avail < sizeof(int64))
+ {
+ return -1;
+ }
+
+ if (fbuf->is_littleendian)
+ {
+ // little endian
+ value = value | (uint64)fbuf->buf[fbuf->index+0] << 0;
+ value = value | (uint64)fbuf->buf[fbuf->index+1] << 8;
+ value = value | (uint64)fbuf->buf[fbuf->index+2] << 16;
+ value = value | (uint64)fbuf->buf[fbuf->index+3] << 24;
+ value = value | (int64)fbuf->buf[fbuf->index+4] << 32;
+ value = value | (int64)fbuf->buf[fbuf->index+5] << 40;
+ value = value | (int64)fbuf->buf[fbuf->index+6] << 48;
+ value = value | (int64)fbuf->buf[fbuf->index+7] << 56;
+ fbuf->index += sizeof(int64);
+ }
+ else
+ {
+ // big endian
+ value = value | (uint64)fbuf->buf[fbuf->index+7] << 0;
+ value = value | (uint64)fbuf->buf[fbuf->index+6] << 8;
+ value = value | (uint64)fbuf->buf[fbuf->index+5] << 16;
+ value = value | (uint64)fbuf->buf[fbuf->index+4] << 24;
+ value = value | (uint64)fbuf->buf[fbuf->index+3] << 32;
+ value = value | (uint64)fbuf->buf[fbuf->index+2] << 40;
+ value = value | (uint64)fbuf->buf[fbuf->index+1] << 48;
+ value = value | (uint64)fbuf->buf[fbuf->index+0] << 56;
+ fbuf->index += sizeof(int64);
+ }
+ *out = value;
+ return 0;
+}
+
+
+/**
+ * etch_flexbuf_get_float()
+ * @return a float from the next available bytes.
+ */
+int etch_flexbuf_get_float(etch_flexbuffer *fbuf, float* out)
+{
+ float value;
+ if (-1 == etch_flexbuf_get_int(fbuf, (int*) &value)) return -1;
+ *out = value;
+ return 0;
+}
+
+
+/**
+ * etch_flexbuf_get_double()
+ * @return a double from the next available bytes.
+ */
+int etch_flexbuf_get_double(etch_flexbuffer *fbuf, double* out)
+{
+ double value;
+ if (-1 == etch_flexbuf_get_long(fbuf, (int64*) &value)) return -1;
+ *out = value;
+ return 0;
+}
+
+
+
+/**
+ * fills a buffer fully from the next available bytes.
+ * @param b
+ * @throws IOException if avail() < b.datalen.
+ */
+size_t etch_flexbuf_get_fully( etch_flexbuffer *fbuf, byte *b, size_t bufsize )
+{
+ return etch_flexbuf_get( fbuf, b, 0, bufsize );
+}
+
+
+/**
+ * If index has moved past length during a put, then adjust length
+ * to track index.
+ */
+static void etch_flexbuf_fix_length(etch_flexbuffer *fbuf)
+{
+ if (fbuf->index > fbuf->datalen)
+ fbuf->datalen = fbuf->index;
+}
+
+
+/**
+ * Puts some bytes into the buffer as if by repeated calls to put().
+ * @param buf the source of the bytes to put.
+ * @param off the index to the first byte to put.
+ * @param bytecount the number of bytes to put.
+ * @return count of bytes put
+ */
+size_t etch_flexbuf_put (etch_flexbuffer *fbuf, byte *buf, size_t off, size_t bytecount)
+{
+ if (bytecount <= 0) return 0;
+ if (!etch_flexbuf_ensure_size (fbuf, fbuf->index + bytecount)) return 0;
+
+ memcpy(fbuf->buf + fbuf->index, buf + off, bytecount);
+ fbuf->index += bytecount;
+ etch_flexbuf_fix_length (fbuf);
+ return bytecount;
+}
+
+
+/**
+ * Copies the specified number of bytes from buf[index] into buffer
+ * as if by repeated execution of put( buf.get() ).
+ * @param buf the source of the bytes to put.
+ * @param len the number of bytes to put. if -1, copy everything.
+ * @return number of bytes put
+ */
+size_t etch_flexbuf_put_from(etch_flexbuffer *dstfb, etch_flexbuffer *srcfb, size_t bytecount)
+{
+ size_t bytes_put = 0;
+ if (bytecount == -1)
+ bytecount = srcfb->datalen - srcfb->index;
+
+ bytes_put = etch_flexbuf_put (dstfb, srcfb->buf, srcfb->index, bytecount);
+
+ etch_flexbuf_skip (srcfb, bytes_put, FALSE);
+
+ return bytes_put;
+}
+
+
+/**
+ * etch_flexbuf_put_byte()
+ * @return number of bytes put
+ */
+size_t etch_flexbuf_put_byte (etch_flexbuffer *fbuf, byte value)
+{
+ size_t bytes_put = 0;
+
+ if (etch_flexbuf_ensure_size(fbuf, fbuf->index + 1))
+ {
+ fbuf->buf[fbuf->index++] = value;
+ etch_flexbuf_fix_length(fbuf);
+ bytes_put = sizeof(value);
+ }
+
+ return bytes_put;
+}
+
+
+/**
+ * etch_flexbuf_put_short()
+ */
+size_t etch_flexbuf_put_short(etch_flexbuffer *fbuf, short value)
+{
+ if (!etch_flexbuf_ensure_size( fbuf, fbuf->index + sizeof(value) ))
+ return 0;
+
+ if (fbuf->is_littleendian)
+ {
+ fbuf->buf[fbuf->index++] = (byte) value;
+ fbuf->buf[fbuf->index++] = (byte) (value >> 8);
+ }
+ else
+ {
+ fbuf->buf[fbuf->index++] = (byte) (((unsigned short)value) >> 8);
+ fbuf->buf[fbuf->index++] = (byte) value;
+ }
+
+ etch_flexbuf_fix_length(fbuf);
+
+ return sizeof(value);
+}
+
+
+/**
+ * etch_flexbuf_put_int()
+ */
+size_t etch_flexbuf_put_int( etch_flexbuffer *fbuf, int value )
+{
+ if (!etch_flexbuf_ensure_size( fbuf, fbuf->index + sizeof(int) ))
+ return 0;
+
+ if (fbuf->is_littleendian)
+ {
+ fbuf->buf[fbuf->index++] = (byte) value;
+ fbuf->buf[fbuf->index++] = (byte) (value >> 8);
+ fbuf->buf[fbuf->index++] = (byte) (value >> 16);
+ fbuf->buf[fbuf->index++] = (byte) (value >> 24);
+ }
+ else
+ {
+ fbuf->buf[fbuf->index++] = (byte) ( ((unsigned int) value) >> 24);
+ fbuf->buf[fbuf->index++] = (byte) ( ((unsigned int) value) >> 16);
+ fbuf->buf[fbuf->index++] = (byte) ( ((unsigned int) value) >> 8);
+ fbuf->buf[fbuf->index++] = (byte) value;
+ }
+
+ etch_flexbuf_fix_length(fbuf);
+
+ return sizeof(value);
+}
+
+
+/**
+ * etch_flexbuf_put_long()
+ */
+size_t etch_flexbuf_put_long(etch_flexbuffer *fbuf, int64 value )
+{
+ if (!etch_flexbuf_ensure_size( fbuf, fbuf->index + sizeof(value) ))
+ return 0;
+
+ if (fbuf->is_littleendian)
+ {
+ fbuf->buf[fbuf->index++] = (byte) value;
+ fbuf->buf[fbuf->index++] = (byte) (value >> 8);
+ fbuf->buf[fbuf->index++] = (byte) (value >> 16);
+ fbuf->buf[fbuf->index++] = (byte) (value >> 24);
+ fbuf->buf[fbuf->index++] = (byte) (value >> 32);
+ fbuf->buf[fbuf->index++] = (byte) (value >> 40);
+ fbuf->buf[fbuf->index++] = (byte) (value >> 48);
+ fbuf->buf[fbuf->index++] = (byte) (value >> 56);
+ }
+ else
+ {
+ fbuf->buf[fbuf->index++] = (byte) ( ((uint64) value ) >> 56);
+ fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 48);
+ fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 40);
+ fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 32);
+ fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 24);
+ fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 16);
+ fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 8);
+ fbuf->buf[fbuf->index++] = (byte) value;
+ }
+
+ etch_flexbuf_fix_length(fbuf);
+
+ return sizeof(value);
+}
+
+
+/**
+ * etch_flexbuf_put_float()
+ */
+size_t etch_flexbuf_put_float( etch_flexbuffer *fbuf, float value )
+{
+ /* return etch_flexbuf_put_int( fbuf, * ((int *)( &value )) ); */
+ const unsigned int u = * ( (int*)&value );
+ return etch_flexbuf_put_int(fbuf, u);
+}
+
+
+/**
+ * etch_flexbuf_put_double()
+ */
+size_t etch_flexbuf_put_double(etch_flexbuffer *fbuf, double value )
+{
+ /* return etch_flexbuf_put_long( fbuf, *( (int64*) &value ) ); */
+ const int64 u = * ( (int64*)&value );
+ return etch_flexbuf_put_long(fbuf, u);
+}
+
+
+/**
+ * Adjusts index as if by a get or put but without transferring
+ * any data. This could be used to skip over a data item in an
+ * input or output buffer.
+ *
+ * @param len the number of bytes to skip over. Len must be
+ * >= 0. If put is false, it is an error if len > avail().
+ * If put is true, the buffer may be extended (and the buffer
+ * length adjusted).
+ *
+ * @param put if true it is ok to extend the length of the
+ * buffer.
+ *
+ * @return this flex buffer object.
+ *
+ * @throws EOFException if put is false and len > avail().
+ *
+ * @throws IOException if the max buffer size is exceeded.
+ */
+etch_flexbuffer *etch_flexbuf_skip(etch_flexbuffer *fbuf, size_t len, int put )
+{
+ if (len < 0) return NULL;
+ if (len == 0) return fbuf;
+
+ if (put)
+ {
+ etch_flexbuf_ensure_size( fbuf, fbuf->index+len );
+ fbuf->index += len;
+ etch_flexbuf_fix_length(fbuf);
+ return fbuf;
+ }
+
+ fbuf->index += len;
+ return fbuf;
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_general.c b/binding-c/runtime/c/src/main/common/etch_general.c
new file mode 100644
index 0000000..15d487a
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_general.c
@@ -0,0 +1,161 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_general.h -- general stuff.
+ */
+
+#include "etch_general.h"
+#include "etch_objecttypes.h"
+#include "etch_hash.h"
+#include "etch_arraylist.h"
+#include <wchar.h>
+
+
+// current offset from CLASSID_DYNAMIC_START (etchobjtypes.h)
+// TODO: make atomic
+// TODO: check range
+static unsigned short g_etch_curr_classid;
+
+//TODO: make atomic
+unsigned short get_dynamic_classid()
+{
+ if (g_etch_curr_classid == 0)
+ g_etch_curr_classid = CLASSID_DYNAMIC_START;
+
+ return g_etch_curr_classid++;
+}
+
+//TODO: make atomic
+unsigned short get_dynamic_classid_unique(unsigned short* globalid)
+{
+ if (*globalid == 0)
+ *globalid = get_dynamic_classid();
+ return (*globalid);
+}
+
+char* strtrim(char* str)
+{
+ char *startpos = str;
+ {
+ // left trim
+ if(str != NULL) {
+ size_t i = 0;
+ size_t l = strlen(str);
+ for(i = 0; i < l; i++) {
+ if(str[i] == ' ' || str[i] == '\t') {
+ continue;
+ }
+ startpos = &str[i];
+ break;
+ }
+ }
+ }
+
+ {
+ // right trim
+ if(startpos != NULL) {
+ size_t i = 0;
+ for(i = strlen(startpos); i > 0; i--) {
+ if(startpos[i-1] == ' ' || startpos[i-1] == '\n') {
+ startpos[i-1] = '\0';
+ continue;
+ }
+ break;
+ }
+ }
+ }
+ return startpos;
+}
+
+void waitkey()
+{
+ printf("any key ...\n");
+#ifndef _WIN32_WCE
+ while(!getc(stdin)) {}
+#else
+ MessageBox(NULL, L"Press any key!", L"Waiting...", NULL);
+#endif
+}
+
+/**
+ * hexchar_to_int()
+ */
+int hexchar_to_int (const unsigned char hexchar)
+{
+ switch(hexchar)
+ { case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ case 'a': case 'A': return 10;
+ case 'b': case 'B': return 11;
+ case 'c': case 'C': return 12;
+ case 'd': case 'D': return 13;
+ case 'e': case 'E': return 14;
+ case 'f': case 'F': return 15;
+ }
+ return -1;
+}
+
+
+/**
+ * hexwchar_to_int()
+ */
+int hexwchar_to_int (const wchar_t hexwchar)
+{
+ switch(hexwchar)
+ { case L'0': return 0;
+ case L'1': return 1;
+ case L'2': return 2;
+ case L'3': return 3;
+ case L'4': return 4;
+ case L'5': return 5;
+ case L'6': return 6;
+ case L'7': return 7;
+ case L'8': return 8;
+ case L'9': return 9;
+ case L'a': case L'A': return 10;
+ case L'b': case L'B': return 11;
+ case L'c': case L'C': return 12;
+ case L'd': case L'D': return 13;
+ case L'e': case L'E': return 14;
+ case L'f': case L'F': return 15;
+ }
+ return -1;
+}
+
+
+int etch_snwprintf(wchar_t* buffer, size_t count, const wchar_t *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+#if defined(_WIN32)
+ return _vsnwprintf(buffer, count, format, args);
+#elif defined(__APPLE__) || defined(__QNX__) || defined(__LINUX__)
+ return vswprintf(buffer, count, format, args);
+#else
+ return vswprintf(buffer, /*count,*/ format, args);
+#endif
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_hash.c b/binding-c/runtime/c/src/main/common/etch_hash.c
new file mode 100644
index 0000000..4b95905
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_hash.c
@@ -0,0 +1,1143 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ etch_hash.c -- implementation of an underlying hashtable.
+ provided herein are implementations for the jenkins hashtable APIs.
+*/
+
+#include "etch_hash.h"
+#include "etch_cache.h"
+#include "etch_mem.h"
+#include "etch_mutex.h"
+#include "etch_objecttypes.h"
+#include "etch_arraylist.h"
+#include "jenkhtab.h"
+#include "jenklook.h"
+
+#define ETCH_HASH_MAX_KEYLENGTH 1024 /* arbitrary max byte length of a hashtable key */
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/**
+ * implementation of etch_hashtable.insert() for the jenkins hashtable.
+ * key and data are pointers to memory owned by the caller. The hashtable does
+ * not make copies of this data. The caller is responsible for freeing said
+ * memory, however note that etch_hashtable.clear() frees this memory.
+ * key cannot be null but data can be null.
+ * datalen parameter is ignored for this implementation.
+ * if out is non_null, *out is assumed to point at a etch_hashitem struct,
+ * and this struct is populated with pointers to the inserted item.
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_insert(void* realtable, void* key, const int keylen,
+ void* data, const int datalen, void* in, void** out)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = 0;
+
+ etch_hashtable* ht = (etch_hashtable*)in;
+
+ if (!realtable || !key || !keylen)
+ return -1;
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_lock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (FALSE == hadd((htab*)realtable, key, keylen, data)) /* jenkhash.lib */
+ result = -1; /* key already exists most likely */
+ else
+ if (out)
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = hkey ((htab*)realtable);
+ outentry->value = hstuff((htab*)realtable);
+ outentry->hash = ((htab*)realtable)->ipos->hval;
+ }
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/**
+ * implementation of etch_hashtable.inserth() for the jenkins hashtable.
+ * key and data are pointers to memory owned by the caller. the hashtable does
+ * not make copies of this data. the caller is responsible for freeing said
+ * memory, however note that etch_hashtable.clear() frees this memory.
+ * key cannot be null but data can be null.
+ * key object is expected to contain its hashkey value in its first 4 bytes.
+ * jenkins will use this value, rather than compute a hash value.
+ * if out is non_null, *out is assumed to point at a etch_hashitem struct,
+ * and this struct is populated with pointers to the inserted item.
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_inserth(void* realtable, void* key, void* data, void* in, void** out)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+
+ if (!realtable || !key)
+ return -1;
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_lock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (FALSE == haddx((htab*)realtable, key, data)) /* jenkhash.lib */
+ result = -1; /* key already exists most likely */
+ else
+ if (out)
+ {
+ etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = hkey ((htab*)realtable);
+ outentry->value = hstuff((htab*)realtable);
+ outentry->hash = ((htab*)realtable)->ipos->hval;
+ }
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/**
+ * implementation of etch_hashtable.find() for the jenkins hashtable.
+ * moves the current position on the underlying table to that of supplied key.
+ * if out is non_null, *out is assumed to point at a etch_hashitem struct,
+ * and this struct is populated with pointers to the located item's key and value.
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_find(void* realtable, void* key, const int keylen,
+ void* in, void** out)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+
+ if (!realtable || !key || !keylen)
+ return -1;
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_lock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (FALSE == hfind((htab*)realtable, (unsigned char*)key, keylen))
+ result = -1;
+ else
+ if (out)
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = hkey ((htab*)realtable);
+ outentry->value = hstuff((htab*)realtable);
+ outentry->hash = ((htab*)realtable)->ipos->hval;
+ }
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/**
+ * implementation of etch_hashtable.findh() for the jenkins hashtable.
+ * Implements a find by hashed key, otherwise see comments for find().
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_findh(void* realtable, const unsigned int hashed,
+ void* in, void** out)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+
+ if (!realtable || !hashed)
+ return -1;
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_lock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (FALSE == hfindx((htab*)realtable, hashed))
+ result = -1;
+ else
+ if (out)
+ { char* tkey = hkey ((htab*)realtable);
+ void* data = hstuff((htab*)realtable);
+ etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = tkey;
+ outentry->value = data;
+ }
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+
+/**
+ * implementation of etch_hashtable.first() for the jenkins hashtable.
+ * moves the current position on the underlying table to that of the first item.
+ * If out is non_null, *out is assumed to point at a etch_hashitem struct,
+ * and this struct is populated with pointers to the located item's key and value.
+ * in parameter is ignored for this implementation.
+ * result is zero if OK, otherwise -1, indicating bad params or an empty table.
+ * @note this method is NOT SYNCHRONIZED, since in most or all cases it is invoked
+ * only during iteration of the map, and in that case the map is locked explicitly
+ * by the caller (hashtable_setlock()), and the mutex may not support nesting.
+ * if there is a need to synch it otherwise, wrap the call as follows:
+ * map->synchook(ETCH_SYNC_SET_, ((etch_object*)map)->synclock);
+ * map->first(...);
+ * map->synchook(ETCH_SYNC_REL_, ((etch_object*)map)->synclock);
+ */
+int jenkins_first(void* realtable, void* in, void** out)
+{
+ int result = 0;
+
+ if (FALSE == hfirst((htab*)realtable))
+ result = -1; /* table is empty most likely */
+ else
+ if (out)
+ { char* tkey = hkey ((htab*)realtable);
+ void* data = hstuff((htab*)realtable);
+ etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = tkey;
+ outentry->value = data;
+ }
+
+ return result;
+}
+
+
+/**
+ * implementation of etch_hashtable.next() for the jenkins hashtable.
+ * Moves the current position on the underlying table to that of the next item
+ * in the table. If out is non_null, *out is assumed to point at a etch_hashitem,
+ * and this struct is populated with pointers to the located item's key and value.
+ * in parameter is ignored for this implementation.
+ * result is zero if OK, otherwise -1, indicating either bad params, or that
+ * there are no more entries, in which case the current position will have wrapped
+ * to the first item, if any entries in fact still remain.
+ * @note this method is NOT SYNCHRONIZED, since in most or all cases it is invoked
+ * only during iteration of the map, and in that case the map is locked explicitly
+ * by the caller (hashtable_setlock()), and the mutex may not support nesting.
+ * if there is a need to synch it otherwise, wrap the call as follows:
+ * map->synchook(ETCH_SYNC_SET_, ((etch_object*)map)->synclock);
+ * map->next(...);
+ * map->synchook(ETCH_SYNC_REL_, ((etch_object*)map)->synclock);
+ */
+int jenkins_next(void* realtable, void* in, void** out)
+{
+ int is_next_found = 0, is_table_empty = 0;
+ char* tkey = NULL;
+ void* data = NULL;
+ if (!realtable) return -1;
+
+ /* hnext returns 1 if there is a next entry, or 0 if there is no next entry
+ * and the position has wrapped to the beginning of the table. However if
+ * the table is now empty, there is no current position, and so we test for
+ * that condition before attempting to reference the current position.
+ */
+ is_next_found = hnext((htab*)realtable); /* jenkhash.h */
+ is_table_empty = NULL == ((htab*)realtable)->ipos;
+
+ if (out) /* return data at current position if requested */
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+
+ if (!is_table_empty)
+ { tkey = hkey ((htab*)realtable);
+ data = hstuff((htab*)realtable);
+ }
+ outentry->key = tkey;
+ outentry->value = data;
+ }
+
+ return is_next_found? 0: -1;
+}
+
+
+/**
+ * implementation of etch_hashtable.current() for the jenkins hashtable.
+ * retrieves data for the entry at the current hashtable position.
+ * *out is assumed to point at a etch_hashitem struct; this struct is populated
+ * with pointers to the current item's key and value.
+ * in parameter is ignored for this implementation.
+ * result is zero if OK, otherwise -1, indicating bad params or an empty table.
+ * @note this method is NOT SYNCHRONIZED, since it is not meaningful for a shared
+ * hashtable (current position will change with every operation).
+ * if there is a need to synch it, wrap the call as follows:
+ * map->synchook(ETCH_SYNC_SET_, ((etch_object*)map)->synclock);
+ * map->current(...);
+ * map->synchook(ETCH_SYNC_REL_, ((etch_object*)map)->synclock);
+ */
+int jenkins_current(void* realtable, void* in, void** out)
+{
+ unsigned hash = 0;
+ char* tkey = NULL;
+ void* data = NULL;
+ etch_hashitem* outentry = NULL;
+ if (!realtable || !out || !((htab*)realtable)->count) return -1;
+
+ tkey = hkey ((htab*)realtable);
+ data = hstuff((htab*)realtable);
+ hash =((htab*)realtable)->ipos->hval;
+
+ outentry = (etch_hashitem*) *out;
+ outentry->key = tkey;
+ outentry->value = data;
+ outentry->hash = hash;
+
+ return tkey? 0: -1;
+}
+
+/**
+ * Implementation of etch_hashtable.remove() for the jenkins hashtable.
+ * Frees the entry for the key supplied , but neither the key nor the value,
+ * are freed, recall that neither of these is a copy but are instead pointers.
+ * To actually free memory for these items, pass etch_hashitem** &out),
+ * and you can then free(out->key), and free(out->value), at your leisure.
+ * Result is zero if OK, otherwise -1;
+ */
+int jenkins_remove(void* realtable, void* key, const int keylen,
+ void* in, void** out)
+{
+ etch_status_t status = ETCH_SUCCESS;
+
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+
+ if (!realtable || !key || !keylen)
+ return -1;
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_lock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (FALSE == hfind((htab*)realtable, key, keylen)) /* locate entry */
+ result = -1; /* key nonexistent most likely */
+ else
+ { if (out) /* save off the entry contents if requested */
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = hkey ((htab*)realtable);
+ outentry->value = hstuff((htab*)realtable);
+ outentry->hash = ((htab*)realtable)->ipos->hval;
+ }
+
+ hdel((htab*)realtable); /* delete entry at current position */
+ }
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+/**
+ * jenkins_removeh()
+ */
+int jenkins_removeh(void* realtable, const unsigned key, void* in, void** out)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = 0;
+ etch_hashtable* ht = (etch_hashtable*) in;
+
+ if (!realtable || !key)
+ return -1;
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_lock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (FALSE == hfindx((htab*)realtable, key)) /* locate entry */
+ result = -1;
+ else
+ { if (out)
+ { etch_hashitem* outentry = (etch_hashitem*) *out;
+ outentry->key = hkey ((htab*)realtable);
+ outentry->value = hstuff((htab*)realtable);
+ }
+
+ hdel((htab*)realtable); /* delete entry at current position */
+ }
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return result;
+}
+
+/**
+ * implementation of etch_hashtable.clear() for the jenkins hashtable.
+ * empties the table and, if requested, frees memory for keys/and/or values.
+ * out parameter is ignored for this implementation.
+ * Result is count of table entries freed, or -1 if error.
+ * Use the freeuser parameter with care. recall that the hashtable stores
+ * pointers to keys and data, plus key length. If user did not allocate each
+ * key separately then setting freeuser would cause a crash. for example,
+ * if I used a vector of keys and a vector of key lengths; setting freeuser
+ * would ask to free (key length) bytes at some vector offset, obviously
+ * an error. also, currently freeuser does not check the memtable, so if
+ * allocations are being tracked, freeuser should not be used. we could
+ * change this code to query the memtable first, or alternatively, change
+ * etch_free to not complain about allegedly missing memtable entries.
+ */
+int jenkins_clear (void* realtable, const int freekey, const int freeval, void* in, void** out)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int freecount = 0, currcount = 0, freehandled = 0;
+ int is_static_keys = 0, is_static_values = 0, is_etch_free = 0;
+ mapcallback callback = NULL;
+ char* key, *value;
+ htab* jenktable;
+ etch_hashtable* ht = (etch_hashtable*) in;
+
+ if (!(jenktable = (htab*)realtable))
+ return -1;
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_lock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ if (ht)
+ { is_static_keys = ht->is_readonly_keys;
+ is_static_values = ht->is_readonly_values;
+ is_etch_free = ht->is_tracked_memory;
+ callback = ht->freehook;
+ }
+
+ while (0 < (currcount = hcount(jenktable)))
+ {
+ key = hkey(jenktable); /* free entry's key if asked */
+ value = hstuff(jenktable);
+ /* optional callback to handle free */
+ freehandled = callback? callback(key, value): FALSE;
+
+ if (freekey && !is_static_keys && !freehandled)
+ if(is_etch_free)
+ etch_free(key);
+ else
+ free(key);
+
+ if (freeval && !is_static_values && !freehandled)
+ if (is_etch_free)
+ etch_free(value);
+ else free(value);
+
+ hdel(jenktable); /* free current table slot */
+ freecount++;
+ }
+
+ if (ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return freecount; /* return count of items freed */
+}
+
+/**
+ * implementation of etch_hashtable.count() for the jenkins hashtable.
+ * in and out parameters are ignored for this implementation.
+ * result is current number of table entries, or -1 if error.
+ */
+int jenkins_count(void* realtable, void* in, void** out)
+{
+ const int count = realtable? ((htab*)realtable)->count: -1;
+ return count;
+}
+
+/**
+ * implementation of etch_hashtable.size() for the jenkins hashtable.
+ * in and out parameters are ignored for this implementation.
+ * result is current maximum number of table entries, or -1 if error.
+ */
+int jenkins_size(void* realtable, void* in, void** out)
+{
+ const int count = realtable? 1 << ((htab*)realtable)->logsize: -1;
+ return count;
+}
+
+/**
+ * implementation of etch_hashtable.stats() for the jenkins hashtable.
+ * in and out parameters are ignored for this implementation.
+ * result is current maximum number of table entries, or -1 if error.
+ */
+int jenkins_stats(void* realtable, void* in, void** out)
+{
+ if (realtable) hstat((htab*)realtable);
+ return realtable? 0: -1;
+}
+
+/**
+ * jenkins_hash
+ * implementation of etch_hashtable.hash() for the jenkins hashtable.
+ * priorhash is result of the previous operation, or any arbitrary value.
+ * in and out parameters are ignored for this implementation. result is a
+ * hash of the supplied key, as used by the jenkins hashtable, or zero
+ * if parameters were in error.
+ * author's comments: If you need less than 32 bits, use a bitmask.
+ * for example, if you need only 10 bits, do h = (h & hashmask(10)),
+ * in which case, the hash table should have hashsize(10) elements.
+ * if you are hashing n strings (unsigned char**)k, do it like this:
+ * for (i=0, h=0; i < n; ++i) h = lookup(k[i], len[i], h);
+ */
+int jenkins_hash(void* realtable, char* key, const int keylen, const int priorhash, void* in, void** out)
+{
+ if (!realtable || !key || keylen < 1 || keylen > ETCH_HASH_MAX_KEYLENGTH) {
+ //TODO: log error
+ return 0;
+ }
+ return lookup(key, keylen, priorhash);
+}
+
+/**
+ * jenkins_hashx
+ * see comments at jenkins_hash
+ */
+int jenkins_hashx(char* key, const int keylen, const int priorhash)
+{
+ if (!key || keylen < 1 || keylen > ETCH_HASH_MAX_KEYLENGTH) return 0;
+ return lookup(key, keylen, priorhash);
+}
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * constructors, destructors
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * constructor for a hashtable implementation. implements iterable.
+ * creates the underlying hashtable and returns a populated etch_hashtable
+ * interface to it. initialsize is the number of items the hashtable can hold
+ * before it reallocates itself. note that this value may be altered by the
+ * implementation, e.g. if it is out of range or if it is not a power of 2.
+ */
+etch_hashtable* new_hashtable(const unsigned int initialsize)
+{
+ etch_hashtable* hashtable = ctor_jenkins_hashtable(initialsize);
+
+ new_iterable(&hashtable->iterable, NULL, hashtable_iterable_first, hashtable_iterable_next, hashtable_iterable_has_next);
+
+ hashtable->is_readonly_keys = HASHTABLE_DEFAULT_READONLY_KEYS;
+ hashtable->is_readonly_values = HASHTABLE_DEFAULT_READONLY_VALUES;
+ hashtable->is_tracked_memory = HASHTABLE_DEFAULT_TRACKED_MEMORY;
+ hashtable->content_type = HASHTABLE_DEFAULT_CONTENT_TYPE;
+
+ return hashtable;
+}
+
+etch_hashtable* new_hashtable_synchronized(const unsigned int initialsize)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_hashtable* hashtable = NULL;
+
+ hashtable = new_hashtable(initialsize);
+ // TODO: pool
+ status = etch_mutex_create(&((etch_object*)hashtable)->synclock, ETCH_MUTEX_NESTED, NULL);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ return hashtable;
+}
+
+/**
+ * constructor for etch_set, which is a hashtable containing object keys
+ * and null values
+ */
+etch_set* new_set (const int initialsize)
+{
+ etch_set* newset = new_hashtable_synchronized(initialsize);
+ newset->content_type = ETCHHASHTABLE_CONTENT_OBJECT_NONE;
+ newset->is_readonly_values = TRUE;
+ return newset;
+}
+
+/**
+ * destructor for a hashtable implementation.
+ * is_free_keys parameter asks that memory pointed to by hashbucket keys be freed.
+ * Use this with care, since this obviously requires that you have malloc'ed keys
+ * individually, and did not, for example, hash your keys out of a memory vector,
+ * static variables, or the like. is_free_values likewise for the table content.
+ * The readonly flags set on the hashtable take precedence over either.
+ * Use of either of course means your pointers to content will now be dangling.
+ */
+int destroy_hashtable(etch_hashtable* map, const int is_free_keys, const int is_free_values)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int is_free_keymem = 0, is_free_valmem = 0;
+ if (NULL == map) return -1;
+
+ if (((etch_object*)map)->synclock) {
+ status = etch_mutex_trylock(((etch_object*)map)->synclock);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ }
+
+ if (!is_etchobj_static_content(map))
+ {
+ struct i_hashtable* vtab = (struct i_hashtable*)((etch_object*)map)->vtab;
+ is_free_keymem = !map->is_readonly_keys && is_free_keys;
+ is_free_valmem = !map->is_readonly_values && is_free_values;
+
+ /* free all buckets, and also contents only if is_free_contents is set */
+ if (map->realtable && vtab && !is_etchobj_static_content(map))
+ {
+ vtab->clear(map->realtable, is_free_keymem, is_free_valmem, map, 0);
+ vtab->hdestroy(map->realtable, map, 0);
+ }
+ } else {
+ }
+
+ if (((etch_object*)map)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)map)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ if (!is_etchobj_static_content(map)) {
+ status = etch_mutex_destroy(((etch_object*)map)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+ }
+
+ //TODO:
+ // cleanup synclock
+ // maybe it is done by map->synchook(ETCH_SYNC_DEL
+
+ if (!is_etchobj_static_shell(map)) {
+ etch_free(map);
+ }
+
+ return 0;
+}
+
+
+/**
+ * implementation of etch_hashtable.destroy() for the jenkins hashtable.
+ * destroys the table, but not the memory allocated for the individual item
+ * keys and values. use clear(), not destroy(), for that purpose.
+ * out parameter is ignored for this implementation.
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_destroy(void* realtable, void* in, void** out)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_hashtable* ht = (etch_hashtable*) in;
+
+ if (!realtable)
+ return -1;
+
+ if(ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_trylock(((etch_object*)ht)->synclock);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ }
+
+ if (((htab*)realtable)->table)
+ hdestroy((htab*) realtable); /* jenkhash.lib */
+
+ if(ht && ((etch_object*)ht)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)ht)->synclock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ return 0;
+}
+
+
+/**
+ * implementation of etch_hashtable.create() for the jenkins hashtable.
+ * this is the constructor for the underlying hashtable.
+ * we receive initial size in items and convert this to bit width.
+ * If initial size supplied is not a power of 2 we make it so.
+ * jenkins takes a log2 value as size, e.g. size 6 means size is 6 bits wide = 64.
+ * returns in *out, a pointer to a jenkins htab struct describing the underlying table.
+ * in parameter is ignored for this implementation.
+ * result is zero if OK, otherwise -1;
+ */
+int jenkins_create(const int initialsize_items, void* in, void** out)
+{
+ htab* hashtable = NULL;
+ int initialsize_bits_plus1 = 0, initialsize, divby2;
+ if (out == NULL) return -1;
+
+ if (initialsize_items <= 0)
+ initialsize = ETCH_DEFAULT_HASHTABLE_SIZE;
+ else
+ if (initialsize_items < MIN_INITIAL_HASHTABLE_SIZE)
+ initialsize = MIN_INITIAL_HASHTABLE_SIZE;
+ else initialsize = initialsize_items;
+
+ if (initialsize > MAX_INITIAL_HASHTABLE_SIZE)
+ initialsize = MAX_INITIAL_HASHTABLE_SIZE;
+
+ for (divby2 = initialsize; divby2; divby2 >>= 1)
+ initialsize_bits_plus1++;
+
+ hashtable = hcreate(initialsize_bits_plus1 - 1); /* jenkhash.lib */
+
+ *out = hashtable;
+ return hashtable? 0: -1;
+}
+
+
+/**
+ * destroy_hashtable_default()
+ * default destructor for jenkins hashtable
+ */
+int destroy_hashtable_default(etch_hashtable* map)
+{
+ destroy_hashtable(map, !map->is_readonly_keys, !map->is_readonly_values);
+ return 0;
+}
+
+
+/**
+ * clone_hashtable_default()
+ * default copy constructor for jenkins hashtable.
+ * we won't do an implementation at this level, since we would need to also clone
+ * content, and only the instantiator knows key/value sizes
+ */
+etch_hashtable* clone_hashtable_default(etch_hashtable* map)
+{
+ return NULL;
+}
+
+/**
+ * constructor for the etch_hashtable interface. constructs and initializes
+ * the interface shell, but not the underlying hashtable.
+ */
+etch_hashtable* _new_etch_hashtable()
+{
+ etch_hashtable* newht = 0;
+ newht = (etch_hashtable*) new_object(sizeof(etch_hashtable),ETCHTYPEB_HASHTABLE,CLASSID_HASHTABLE);
+ return newht;
+}
+
+/**
+ * ctor_jenkins_hashtable()
+ * constructor for jenkins hashtable interface.
+ * populates interface implementation methods and creates the underlying hashtable.
+ * returns pointer to etch_hashtable, or NULL if table could not be created.
+ */
+etch_hashtable* ctor_jenkins_hashtable(const int initialsize_items)
+{
+ htab* jenkins_hashtable = NULL;
+ etch_hashtable* hashtable = NULL;
+ i_etch_hashtable* vtab = NULL;
+ const unsigned short CLASS_ID = CLASSID_HASHTABLE_VTAB;
+ int result = 0, is_just_cached = FALSE;
+
+ hashtable = _new_etch_hashtable();
+
+ vtab = etch_cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+ if(!vtab)
+ {
+ vtab = new_vtable(((etch_object*)hashtable)->vtab, sizeof(i_etch_hashtable), CLASS_ID);
+ vtab->create = jenkins_create;
+ vtab->hdestroy = jenkins_destroy;
+ vtab->insert = jenkins_insert;
+ vtab->inserth = jenkins_inserth;
+ vtab->find = jenkins_find;
+ vtab->findh = jenkins_findh;
+ vtab->first = jenkins_first;
+ vtab->next = jenkins_next;
+ vtab->current = jenkins_current;
+ vtab->remove = jenkins_remove;
+ vtab->removeh = jenkins_removeh;
+ vtab->clear = jenkins_clear;
+ vtab->count = jenkins_count;
+ vtab->size = jenkins_size;
+ vtab->stats = jenkins_stats;
+
+ etch_cache_insert(((etch_object*)vtab)->get_hashkey(vtab), vtab, FALSE);
+ is_just_cached = TRUE;
+ }
+
+ ((etch_object*)hashtable)->vtab = (vtabmask*)vtab;
+
+ /* create the underlying real hashtable */
+ result = vtab->create(initialsize_items, NULL, &jenkins_hashtable);
+ hashtable->realtable = jenkins_hashtable;
+
+ ((etch_object*)hashtable)->destroy = destroy_hashtable_default;
+ ((etch_object*)hashtable)->clone = clone_hashtable_default;
+
+ if (result == -1)
+ {
+ if (is_just_cached) {
+ etch_cache_del(CLASS_ID);
+ }
+ etch_object_destroy(hashtable);
+ }
+
+ return hashtable;
+}
+
+
+/*
+ * new_systemhashtable()
+ * for such a hashtable we will not use the vtab interface
+ * but rather will call the implementation methods directly.
+ */
+etch_hashtable* new_systemhashtable(const int initialsize_items)
+{
+ int result = 0;
+ htab* jenkins_hashtable = NULL;
+ etch_hashtable* hashtable = 0;
+
+ hashtable = etch_malloc(sizeof(etch_hashtable), 0);
+ memset(hashtable, 0, sizeof(etch_hashtable));
+
+ result = jenkins_create(initialsize_items, NULL, &jenkins_hashtable);
+ hashtable->realtable = jenkins_hashtable;
+
+ if (result == 0)
+ new_iterable(&hashtable->iterable, NULL, hashtable_iterable_first,
+ hashtable_iterable_next, hashtable_iterable_has_next);
+
+ else /* some problem creating hashtable */
+ {
+ delete_systemhashtable(hashtable);
+ hashtable = NULL;
+ }
+
+ return hashtable;
+}
+
+
+/*
+ * delete_systemhashtable()
+ * delete an untracked hashtable
+ */
+void delete_systemhashtable(etch_hashtable* hashtable)
+{
+ if (!hashtable) return;
+ jenkins_destroy(hashtable->realtable, NULL, NULL);
+ free(hashtable);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * hashtable synchronization
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * hashtable_defsynchook()
+ * hashtable synchronization hook usable for most purposes.
+ * to enable synchronization, set map.synchook to this function,
+ * and set map.synclock to an instantiated mutex.
+ */
+//int hashtable_defsynchook(void* action, void* mutex)
+//{
+// /* all the casting is to quash pointer cast warnings */
+// return etch_mutex_default_hookproc((int)(((size_t) action) & 0xf), mutex);
+//}
+
+
+/*
+ * hashtable_getlock()
+ * explicitly set this map's synchronization lock, waiting if unavailable.
+ * this should be used only for locking a map prior to iterating the map.
+ * for synchronization of map operations, the presence of map.synchook
+ * and map.synclock is sufficient.
+ */
+int hashtable_getlock (etch_hashtable* map)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ ETCH_ASSERT(map && ((etch_object*)map)->synclock);
+ if(((etch_object*)map)->synclock) {
+ status = etch_mutex_lock(((etch_object*)map)->synclock);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * hashtable_trylock()
+ * explicitly set this map's synchronization lock, failing if unavailable.
+ * this should be used only for locking a map prior to iterating the map.
+ * for synchronization of map operations, the presence of map.synchook
+ * and map.synclock is sufficient.
+ */
+int hashtable_trylock (etch_hashtable* map)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ ETCH_ASSERT(map && ((etch_object*)map)->synclock);
+ if(((etch_object*)map)->synclock) {
+ status = etch_mutex_trylock(((etch_object*)map)->synclock);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * hashtable_rellock()
+ * release explicitly set this map's synchronization lock.
+ * this should be used only for unlocking a map after iterating the map.
+ * for synchronization of map operations, the presence of map.synchook
+ * and map.synclock is sufficient.
+ */
+int hashtable_rellock (etch_hashtable* map)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ ETCH_ASSERT(map && ((etch_object*)map)->synclock);
+ if(((etch_object*)map)->synclock) {
+ status = etch_mutex_unlock(((etch_object*)map)->synclock);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * i_iterable implementations
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * hashtable_iterable_first()
+ * i_iterable first() implementation
+ */
+int hashtable_iterable_first(etch_iterator* iter)
+{
+ etch_hashtable* hash = NULL;
+ etch_hashitem hashbucket, *outentry = &hashbucket;
+ if (!iter || !iter->collection) return -1;
+ iter->current_key = iter->current_value = NULL;
+ hash = iter->collection;
+
+ if (-1 == ((struct i_hashtable*)((etch_object*)hash)->vtab)->first(hash->realtable, 0, &outentry))
+ return -1;
+
+ iter->current_key = outentry->key;
+ iter->current_value = outentry->value;
+ iter->ordinal++;
+ return 0;
+}
+
+
+/*
+ * hashtable_iterable_next()
+ * i_iterable next() implementation
+ * functions as first() if there is no current position.
+ */
+int hashtable_iterable_next(etch_iterator* iter)
+{
+ etch_hashtable* hash = NULL;
+ etch_hashitem hashbucket, *outentry = &hashbucket;
+ if (!iter || !iter->collection || !iter->ordinal) return -1;
+ iter->current_key = iter->current_value = NULL;
+ hash = iter->collection;
+
+ if (-1 == ((struct i_hashtable*)((etch_object*)hash)->vtab)->next(hash->realtable, 0, &outentry))
+ return -1;
+
+ iter->current_key = outentry->key;
+ iter->current_value = outentry->value;
+ iter->ordinal++;
+ return 0;
+}
+
+
+/*
+ * hashtable_iterable_has_next()
+ * i_iterable has_next() implementation.
+ */
+int hashtable_iterable_has_next(etch_iterator* iter)
+{
+ return iter && iter->collection && iter->current_key && iter->ordinal;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * clear() callbacks
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * string_to_object_clear_handler()
+ * callback set to handle freeing of key/value memory during destroy()
+ * and subsequent clear() of a string-to-etch_object hashtable.
+ * handlers return FALSE to indicate memory management NOT handled,
+ */
+int string_to_object_clear_handler (void* data1, void* data2)
+{
+ wchar_t* key = (wchar_t*)data1;
+ etch_object* value = (etch_object*)data2;
+ etch_free(key); /* free string key */
+ etch_object_destroy(value); /* free etch object value */
+ return TRUE;
+}
+
+
+/*
+ * object_to_object_clear_handler()
+ * callback set to handle freeing of key/value memory during destroy()
+ * and subsequent clear() of a etch_object-to-etch_object hashtable.
+ * handlers return FALSE to indicate memory management NOT handled.
+ */
+int object_to_object_clear_handler (void* data1, void* data2)
+{
+ etch_object* key = (etch_object*)data1;
+ etch_object* value = (etch_object*)data2;
+ etch_object_destroy(key); /* free etch object key */
+ etch_object_destroy(value); /* free etch object value */
+ return TRUE;
+}
+
+
+/*
+ * etch_noop_clear_handler()
+ * callback set to handle freeing of key/value memory during destroy()
+ * and subsequent clear() of a etch_object-to-etch_object hashtable.
+ */
+int etch_noop_clear_handler (void* key, void* value)
+{
+ return TRUE;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * etch_map, etch_set
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * new_etch_map()
+ * an etch_map is a hashtable having object keys and object values.
+ * an object key should of course have its hashkey pre-computed appropriately.
+ * if instantiator wants to use non-disposable objects as keys and/or values,
+ * is_readonly_keys and/or is_readonly_values should be set, and the freehook
+ * callback overridden. furthermore if caller chooses to use the same object
+ * as both key and value, similar steps should be taken to ensure that code
+ * does not try to destroy both key and value.
+ */
+etch_hashtable* new_etch_map(const unsigned int initialsize)
+{
+ etch_hashtable* newmap = new_hashtable(initialsize);
+ newmap->content_type = ETCHHASHTABLE_CONTENT_OBJECT_OBJECT;
+ newmap->freehook = object_to_object_clear_handler;
+ newmap->is_readonly_keys = newmap->is_readonly_values = FALSE;
+ return newmap;
+}
+
+/*
+ * new_etch_set()
+ * an etch_set is a hashtable having object keys and null values.
+ */
+etch_hashtable* new_etch_set(const unsigned int initialsize)
+{
+ etch_hashtable* newset = new_hashtable(initialsize);
+ ((etch_object*)newset)->class_id = CLASSID_ETCH_SET; /* serializer will expect this */
+ newset->content_type = ETCHHASHTABLE_CONTENT_OBJECT_NONE;
+ newset->freehook = object_to_object_clear_handler;
+ newset->is_readonly_keys = FALSE;
+ newset->is_readonly_values = TRUE;
+ return newset;
+}
+
+
+/**
+ * get_map_keys()
+ * return a collection of the specified map's keys.
+ * caller must invoke the destructor on the returned list. the returned list
+ * is marked as readonly content, in order that arraylist_destroy() will not
+ * attempt to free memory for the list content, which remains owned by the map.
+ */
+etch_arraylist* get_map_keys(etch_hashtable* map)
+{
+ etch_iterator iterator;
+ etch_arraylist* list = NULL;
+
+ const int typecount = ((struct i_hashtable*)((etch_object*)map)->vtab)->count(map->realtable,0,0);
+ list = new_etch_arraylist(typecount, 0);
+ list->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
+ list->is_readonly = TRUE; /* content is refs to objects owned by map */
+
+ set_iterator(&iterator, map, &map->iterable);
+
+ while(((struct i_iterable*)iterator.object.vtab)->has_next(&iterator))
+ {
+ etch_arraylist_add(list, iterator.current_key);
+ ((struct i_iterable*)iterator.object.vtab)->next(&iterator);
+ }
+
+ return list;
+}
+
+/**
+ * get_map_values()
+ * return a collection of the specified map's values.
+ * caller must invoke the destructor on the returned list. the returned list
+ * is marked as readonly content, in order that arraylist_destroy() will not
+ * attempt to free memory for the list content, which remains owned by the map.
+ */
+etch_arraylist* get_map_values(etch_hashtable* map)
+{
+ etch_iterator iterator;
+ etch_arraylist* list = NULL;
+
+ const int typecount = ((struct i_hashtable*)((etch_object*)map)->vtab)->count(map->realtable,0,0);
+ list = new_etch_arraylist(typecount, 0);
+ list->content_type = map->content_type;
+ list->is_readonly = TRUE; /* content is refs to objects owned by map */
+
+ set_iterator(&iterator, map, &map->iterable);
+
+ while(((struct i_iterable*)iterator.object.vtab)->has_next(&iterator))
+ {
+ etch_arraylist_add(list, iterator.current_value);
+ ((struct i_iterable*)iterator.object.vtab)->next(&iterator);
+ }
+
+ return list;
+}
+
+
diff --git a/binding-c/runtime/c/src/main/common/etch_hashfunc.c b/binding-c/runtime/c/src/main/common/etch_hashfunc.c
new file mode 100644
index 0000000..b24061b
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_hashfunc.c
@@ -0,0 +1,49 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * hashfunc.c -- implementation of a hash function.
+ * we use the hash function from the jenkins hashtable.
+ */
+
+#include "etch.h"
+#include "jenklook.h"
+
+#define ETCH_HASH_MAX_KEYLENGTH 1024 /* arbitrary max byte length of a hashtable key */
+
+/**
+ * etchhash -- global method to hash an arbitrary byte string to 32 bits.
+ * note that keylen is the key byte length, not string length, as these of
+ * course differ for unicode.
+ * priorhash is result of the previous operation, or any arbitrary value --
+ * see jenkin's comments below for an example.
+ * returns hash of the supplied key, as used by the jenkins hashtable,
+ * or zero if parameters were in error.
+ *
+ * jenkins' comments: if you need less than 32 bits, use a bitmask.
+ * for example, if you need only 10 bits, do h = (h & hashmask(10)),
+ * in which case, the hash table should have hashsize(10) elements.
+ * if you are hashing n strings (unsigned char**)k, do it like this:
+ * for (i=0, h=0; i < n; ++i) h = lookup(k[i], len[i], h);
+*/
+uint32 etchhash(const void* pkey, const int keylen, const unsigned priorhash)
+{ /* jenkhash.lib */
+ if (!pkey || keylen < 1 || keylen > ETCH_HASH_MAX_KEYLENGTH) return 0;
+ return lookup((ub1*)pkey, keylen, priorhash);
+}
+
diff --git a/binding-c/runtime/c/src/main/common/etch_linked_list.c b/binding-c/runtime/c/src/main/common/etch_linked_list.c
new file mode 100644
index 0000000..8748fae
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_linked_list.c
@@ -0,0 +1,670 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_linklist.c -- implementation of linklist.
+ */
+
+#include "etch_linked_list.h"
+
+/*
+TODO:
+- no etch_object currently
+*/
+
+
+struct etch_linked_list_node
+{
+ void* data;
+ struct etch_linked_list_node* next;
+};
+
+typedef struct etch_linked_list_node etch_linked_list_node;
+
+
+struct etch_linked_list_t
+{
+ etch_object object;
+
+ void** impl; /*TODO: check why this is a ** here */
+ unsigned short content_obj_type; /* etch obj_type of a native value */
+ unsigned short content_class_id; /* etch class_id of a native value */
+ unsigned int count;
+
+ /* this object may be masked by etch_collection_mask to determine content
+ * type and class, so do not add any fields above this comment */
+
+ etch_linked_list_node* head;
+ etch_linked_list_node* tail;
+ uint8 flags;
+};
+
+etch_status_t etch_linked_list_create(etch_linked_list_t** list, uint8 flags)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_linked_list_t* newlist = NULL;
+
+ if(list == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ newlist = etch_malloc(sizeof(etch_linked_list_t), ETCHTYPEB_COLLECTION);
+ memset(newlist, 0, sizeof(etch_linked_list_t));
+ newlist->flags = flags;
+
+ *list = newlist;
+
+ return rv;
+}
+
+etch_status_t etch_linked_list_add(etch_linked_list_t* list, void* data)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_linked_list_node* newnode = NULL;
+
+ if(list == NULL || data == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ newnode = etch_malloc(sizeof(etch_linked_list_node), 0);
+ newnode->data = data;
+ newnode->next = NULL;
+
+ if(list->head == NULL) {
+ list->head = newnode;
+ }
+ else {
+ ETCH_ASSERT(list->tail != NULL);
+ list->tail->next = newnode;
+ }
+ list->tail = newnode;
+ list->count++;
+
+ return rv;
+}
+
+
+
+etch_status_t etch_linked_list_insert(etch_linked_list_t* list, int32 index, void* data)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_linked_list_node* newnode = NULL;
+ etch_linked_list_node* node = NULL;
+ uint32 i = 0;
+
+ if(list == NULL ||data == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ if((uint32)index >= list->count || index < 0) {
+ return ETCH_EOUTOFBOUNDS;
+ }
+
+ /* check if index is fist element in the list or last position */
+ if(list->head == NULL) {
+ return etch_linked_list_add(list, data);
+ }
+
+ newnode = etch_malloc(sizeof(etch_linked_list_node), 0);
+ newnode->data = data;
+ newnode->next = NULL;
+
+ rv = ETCH_ERROR;
+ node = list->head;
+ while(node != NULL) {
+ if(index == 0) {
+ newnode->next = list->head;
+ list->head = newnode;
+ rv = ETCH_SUCCESS;
+ break;
+ }
+ if(index - 1 == i) {
+ newnode->next = node->next;
+ node->next = newnode;
+ rv = ETCH_SUCCESS;
+ break;
+ }
+ node = node->next;
+ i++;
+ }
+
+ ETCH_ASSERT(rv == ETCH_SUCCESS);
+ list->count++;
+
+ return rv;
+}
+
+etch_status_t etch_linked_list_get(etch_linked_list_t* list, int32 index, void** data)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_linked_list_node* node = NULL;
+ uint32 i = 0;
+
+ if(list == NULL ||data == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ if((uint32)index >= list->count || index < 0) {
+ return ETCH_EINVAL;
+ }
+
+ node = list->head;
+ while(node != NULL) {
+ if(index == i) {
+ *data = node->data;
+ break;
+ }
+ node = node->next;
+ i++;
+ }
+ return rv;
+}
+
+int32 etch_linked_list_index_of(etch_linked_list_t* list, const void* data)
+{
+ etch_linked_list_node* node = NULL;
+ int32 i = 0;
+ int32 index = -1;
+
+ if(list == NULL ||data == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ node = list->head;
+ while(node != NULL) {
+ if(node->data == data) {
+ index = i;
+ break;
+ }
+ node = node->next;
+ i++;
+ }
+ return index;
+}
+
+uint8 etch_linked_list_contains(etch_linked_list_t* list, const void* data)
+{
+ etch_linked_list_node* node = NULL;
+
+ if(list == NULL || data == NULL) {
+ return FALSE;
+ }
+
+ node = list->head;
+ while(node != NULL) {
+ if(node->data == data) {
+ return TRUE;
+ }
+ node = node->next;
+ }
+ return FALSE;
+}
+
+etch_status_t etch_linked_list_remove_at(etch_linked_list_t* list, const int32 index)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_linked_list_node* rmnode = NULL;
+ etch_linked_list_node* node = NULL;
+ uint32 i = 0;
+
+ if(list == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ if((uint32)index >= list->count || index < 0) {
+ return ETCH_EOUTOFBOUNDS;
+ }
+
+ /* check if only one element available */
+ if(list->count == 1) {
+ if(list->flags & ETCH_LINKED_LIST_DATA_FREE) {
+ etch_free(list->head->data);
+ }
+ etch_free(list->head);
+ list->head = NULL;
+ list->tail = NULL;
+ list->count = 0;
+ return ETCH_SUCCESS;
+ }
+
+ node = list->head;
+ while(node != NULL) {
+ if(index == 0) {
+ rmnode = node;
+ list->head = node->next;
+ break;
+ }
+ if(index - 1 == i && index == list->count - 1 ) {
+ rmnode = node->next;
+ node->next = NULL;
+ list->tail = node;
+ break;
+ }
+ if(index - 1 == i) {
+ rmnode = node->next;
+ node->next = node->next->next;
+ break;
+ }
+ node = node->next;
+ i++;
+ }
+
+ /* delete node */
+ ETCH_ASSERT(rmnode != NULL);
+ if(rmnode != NULL) {
+ if(list->flags & ETCH_LINKED_LIST_DATA_FREE) {
+ etch_free(rmnode->data);
+ }
+ etch_free(rmnode);
+ list->count--;
+ } else {
+ rv = ETCH_ERROR;
+ }
+
+ return rv;
+}
+
+etch_status_t etch_linked_list_remove(etch_linked_list_t* list, void* data)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ int32 index = -1;
+
+ if(list == NULL || data == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ index = etch_linked_list_index_of(list, data);
+ rv = etch_linked_list_remove_at(list, index);
+
+ return rv;
+}
+
+etch_status_t etch_linked_list_clear(etch_linked_list_t* list)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_linked_list_node* node = NULL;
+ etch_linked_list_node* temp = NULL;
+
+ if(list == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ node = list->head;
+ while(node != NULL) {
+ temp = node;
+ node = node->next;
+
+ if(list->flags & ETCH_LINKED_LIST_DATA_FREE) {
+ etch_free(temp->data);
+ }
+ etch_free(temp);
+ }
+
+ list->head = NULL;
+ list->tail = NULL;
+ list->count = 0;
+
+ return rv;
+}
+
+uint32 etch_linked_list_count(etch_linked_list_t* list)
+{
+ if(list == NULL) {
+ return 0;
+ }
+ return list->count;
+}
+
+static etch_status_t etch_linked_list_cleanup(void* p)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_linked_list_t* list = NULL;
+
+ if(p == NULL) {
+ return ETCH_EINVAL;
+ }
+ list = p;
+ etch_linked_list_clear(list);
+ etch_free(list);
+
+ return rv;
+}
+
+etch_status_t etch_linked_list_destroy(etch_linked_list_t* list)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ rv = etch_linked_list_cleanup(list);
+ return rv;
+}
+
+
+
+
+
+
+
+
+
+///**
+// * new_linkist()
+// * constructor for an etch_linklist.
+// * Creates the underlying list and returns a pointer to the list.
+// */
+//etch_linklist* new_linklist()
+//{
+// etch_linklist* list = etch_malloc(sizeof(etch_linklist), ETCHTYPEB_COLLECTION);
+// memset(list, 0, sizeof(etch_linklist));
+// return list;
+//}
+//
+//
+///*
+// * linklist_clear()
+// * remove all content from the arraylist, freeing bucket memory always,
+// * and content memory if requested.
+// */
+//void linklist_clear(etch_linklist* list, const int is_free_content)
+//{
+// linklist_node *p = NULL, *nextp = NULL;
+// if (NULL == list) return;
+//
+// for(p = list->head; p; p = nextp)
+// {
+// if (is_free_content && p->content)
+// etch_free(p->content);
+// nextp = p->next;
+// free(p);
+// }
+//
+// memset(list, 0, sizeof(etch_linklist));
+//}
+//
+//
+///**
+// * linklist_destroy()
+// * Destructor for an etch_linklist.
+// * Destroys the underlying list, the list shell, and the list content if requested.
+// */
+//void linklist_destroy(etch_linklist* list, const int is_free_content)
+//{
+// if (NULL == list) return;
+// linklist_clear(list, is_free_content);
+// etch_free(list);
+//}
+//
+//
+///*
+// * linklist_add()
+// * add a node to the end of the list, returning the new node's index
+// */
+//int linklist_add(etch_linklist* list, void* content)
+//{
+// linklist_node* node = NULL;
+// if (NULL == list) return -1;
+//
+// node = etch_malloc(sizeof(linklist_node), ETCHTYPEB_LINKLIST);
+// node->content = content;
+// node->next = NULL;
+//
+// if (list->head == NULL) /* first entry? */
+// list->head = node;
+// else
+// { assert(list->tail);
+// list->tail->next = node;
+// }
+//
+// list->tail = node;
+// return ++list->count;
+//}
+//
+//
+///*
+// * linklist_insert()
+// * add a node anywhere in the list, returning the new node's index,
+// * or -1 if a parameter was in error.
+// * this implementation cannot insert past the current end of list,
+// * in other words to insert at index n, there must be at least n
+// * entries currently in the list.
+// */
+//int linklist_insert(etch_linklist* list, const unsigned int i, void* content)
+//{
+// unsigned count = 0;
+// linklist_node* newnode = NULL, *p = NULL, *priorp = NULL;
+// if ((NULL == list) || (i > list->count) || (i < 0)) return -1;
+//
+// newnode = etch_malloc(sizeof(linklist_node), ETCHTYPEB_LINKLIST);
+// newnode->content = content;
+// p = priorp = list->head;
+//
+// if (list->head == NULL)
+// list->head = list->tail = newnode;
+// else
+// if (i == list->count)
+// list->tail->next = list->tail = newnode;
+// else
+// { for(p = list->head; p, count < i; p = p->next, count++)
+// priorp = p;
+//
+// newnode->next = priorp->next;
+// priorp->next = newnode;
+// }
+//
+// ++list->count;
+// return i;
+//}
+//
+//
+///*
+// * linklist_moveto()
+// * private method to move to an indicated index in the list.
+// * returns -1 if a parameter was in error, else 0;
+// * returns also in outp, a pointer to the node at the requested position.
+// */
+//int linklist_moveto(etch_linklist* list, const unsigned startat, const linklist_node** outp)
+//{
+// unsigned count = 0;
+// linklist_node* p = NULL;
+// if ((NULL == list) || (startat > list->count) || (startat < 0)) return -1;
+//
+// p = list->head;
+// while(p != NULL && count < startat){
+// p = p->next;
+// count++;
+// }
+// if(p != NULL) {
+// *outp = p;
+// return 0;
+// }else{
+// *outp = NULL;
+// return -1;
+// }
+//}
+//
+//
+///*
+// * linklist_containsp()
+// * return 1 or 0 indicating if the list contains the supplied content pointer,
+// * or -1 if a parameter was in error.
+// */
+//int linklist_containsp(etch_linklist* list, void* content, const unsigned startat)
+//{
+// struct linklist_node *p = NULL;
+//
+// if (-1 == linklist_moveto(list, startat, &p)) return -1;
+//
+// for(; p; p = p->next)
+// if (p->content == content)
+// return TRUE;
+//
+// return FALSE;
+//}
+//
+//
+///*
+// * linklist_indexofp()
+// * if the list contains the supplied content pointer, return its index;
+// * return -1 if not found or if a parameter was in error.
+// */
+//int linklist_indexofp(etch_linklist* list, void* content, const unsigned startat)
+//{
+// struct linklist_node *p = NULL;
+// int count = startat, foundat = -1;
+//
+// if (-1 == linklist_moveto(list, startat, &p)) return -1;
+//
+// for(; p; p = p->next)
+// {
+// if (p->content == content)
+// { foundat = count;
+// break;
+// }
+// else count++;
+// }
+//
+// return foundat;
+//}
+//
+//
+///*
+// * linklist_contains()
+// * return 1 or 0 indicating if the list contains the supplied content
+// * or -1 if a parameter was in error.
+// * caller must supply a comparator function of signature int f(void* this, void* that),
+// * which returns -1 if less, 0 if equal, 1 if greater.
+// */
+//int linklist_contains(etch_linklist* list, void* content, const unsigned startat, etch_comparator compare)
+//{
+// struct linklist_node *p = NULL;
+// int result = 0;
+//
+// if (!list || !compare) return -1;
+// if (-1 == linklist_moveto(list, startat, &p)) return -1;
+//
+// for(; p; p = p->next)
+// {
+// result = compare(content, p->content);
+// if (result == 0) return TRUE;
+// }
+//
+// return FALSE;
+//}
+//
+//
+///*
+// * linklist_indexof()
+// * if the list contains the supplied content pointer, return its index.
+// * return -1 if a parameter was in error.
+// * caller must supply a comparator function of signature: int (*f)(void* this, void* that);
+// * which must return -1 if less, 0 if equal, 1 if greater.
+// */
+//int linklist_indexof(etch_linklist* list, void* content, const unsigned startat, etch_comparator compare)
+//{
+// struct linklist_node *p = NULL;
+// int result = 0, count = startat, foundat = -1;
+//
+// if (!list || !compare) return -1;
+//
+// if (-1 == linklist_moveto(list, startat, &p)) return -1;
+//
+// for(; p; p = p->next)
+// {
+// result = compare(content, p->content);
+// if (result == 0)
+// { foundat = count;
+// break;
+// }
+// else count++;
+// }
+//
+// return foundat;
+//}
+//
+//
+///*
+// * linklist_get()
+// * return entry at specified index position, or NULL if parameter error.
+// * if we find the lists are not generally short, we'll supply an indexed version of this list,
+// * and/or implement a resizable array of pointers which we block memcpy to insert and delete.
+// */
+//linklist_node* linklist_get(etch_linklist* list, const unsigned i)
+//{
+// unsigned count = 0;
+// struct linklist_node *p = NULL;
+// if ((NULL == list) || (i >= list->count) || (i < 0)) return NULL;
+//
+// if(linklist_moveto(list, i, &p) != -1){
+// return p;
+// }
+//
+// return NULL; /* can't arrive here */
+//}
+//
+//
+///*
+// * linklist_remove()
+// * remove entry at specified index position, free bucket memory; free content memory on request
+// * return -1 if a parameter was in error, or zero if OK.
+// */
+//int linklist_remove(etch_linklist* list, const unsigned i, const int is_free_content)
+//{
+// int count = 0;
+// struct linklist_node *p = NULL, *priorp = NULL, *tmp = NULL;
+// if (!list || (i >= list->count) || (i < 0)) return -1;
+//
+// if(i == 0 && list->head){
+// if(list->head == list->tail)
+// list->tail = NULL;
+// tmp = list->head;
+// list->head = list->head->next;
+// } else {
+// p = linklist_get(list,i-1);
+// tmp = p->next;
+// if(p && p->next && p->next->next){
+// p->next = p->next->next;
+// } else if (p->next && !p->next->next){
+// list->tail = p;
+// p->next = NULL;
+// } else {
+// return -1;
+// }
+// }
+//
+// if(tmp) {
+// if (is_free_content && tmp->content)
+// etch_free(tmp->content);
+// etch_free(tmp);
+// if(list->count) {
+// list->count--;
+// }
+// }
+//
+// return 0;
+//}
+//
+//
+//int linklist_size(etch_linklist* list)
+//{
+// linklist_node* cur = list->head;
+// int length = 0;
+// while(cur != NULL) {
+// length++;
+// cur = cur->next;
+// }
+// return length;
+//}
+//
diff --git a/binding-c/runtime/c/src/main/common/etch_log.c b/binding-c/runtime/c/src/main/common/etch_log.c
new file mode 100644
index 0000000..5a01077
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_log.c
@@ -0,0 +1,346 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_log.c
+ * etch c binding logger
+ */
+
+#include "etch_log.h"
+#include "etch_runtime.h"
+#include "etch_mem.h"
+#include "etch_general.h"
+#include "etch_thread.h"
+
+#define ETCH_LOG_MAX_APPENDER 5
+
+static const char* etch_log_level_str[] = {
+ "XDEBUG",
+ "DEBUG",
+ "INFO",
+ "WARN",
+ "ERROR"
+};
+
+//
+// etch console appender
+//
+etch_status_t etch_log_appender_console_create(void** appender, etch_config_t* config)
+{
+ *appender = NULL;
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_log_appender_console_open(void* appender)
+{
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_log_appender_console_log(void* appender, etch_log_message_t* logmsg)
+{
+ struct tm* ts = NULL;
+
+ if(logmsg == NULL) {
+ return ETCH_EINVAL;
+ }
+ // log message
+ // 23-09-2009 02:52:01 [012345] XDEBUG etch_thread - Located nearest gas station - etch_thread.h, 2223
+ ts = localtime(&logmsg->timestamp);
+ printf("\r%02d-%02d-%04d %02d:%02d:%02d [%06d] %-6s %-25.25s - %s - %s %d\n",
+ ts->tm_mday,
+ ts->tm_mon + 1,
+ ts->tm_year +1900,
+ ts->tm_hour,
+ ts->tm_min,
+ ts->tm_sec,
+ logmsg->threadid,
+ etch_log_level_str[logmsg->level],
+ logmsg->category,
+ logmsg->message,
+ logmsg->file,
+ logmsg->line
+ );
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_log_appender_console_close(void* appender)
+{
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_log_appender_console_destroy(void* appender)
+{
+ return ETCH_SUCCESS;
+}
+
+static struct etch_log_appender_desc etch_log_appender_console_desc = {
+ etch_log_appender_console_create,
+ etch_log_appender_console_open,
+ etch_log_appender_console_log,
+ etch_log_appender_console_close,
+ etch_log_appender_console_destroy
+};
+
+//
+// etch file appender
+//
+
+struct etch_log_appender_file_t
+{
+ FILE* file;
+};
+
+etch_status_t etch_log_appender_file_create(void** appender, etch_config_t* config)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ // add custome data here
+ *appender = etch_malloc(sizeof(struct etch_log_appender_file_t), 0);
+ ETCH_ASSERT(*appender != NULL);
+ return status;
+}
+
+etch_status_t etch_log_appender_file_open(void* appender)
+{
+ // check if file exists
+ // save file inside archiv
+ // open logfile
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_log_appender_file_log(void* appender, etch_log_message_t* message)
+{
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_log_appender_file_close(void* appender)
+{
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_log_appender_file_destroy(void* appender)
+{
+ if(appender != NULL) {
+ etch_free(appender);
+ }
+ return ETCH_SUCCESS;
+}
+
+static struct etch_log_appender_desc etch_log_appender_file_desc = {
+ etch_log_appender_file_create,
+ etch_log_appender_file_open,
+ etch_log_appender_file_log,
+ etch_log_appender_file_close,
+ etch_log_appender_file_destroy
+};
+
+struct etch_log_appender_desc_entry {
+ const char* name;
+ etch_log_appender_desc* desc;
+};
+
+static struct etch_log_appender_desc_entry appenders[] = {
+ {"consoleappender", &etch_log_appender_console_desc},
+ {"fileappender", &etch_log_appender_file_desc},
+ {NULL, NULL},
+ {NULL, NULL},
+ {NULL, NULL}
+};
+
+struct etch_log_t
+{
+ etch_log_level level;
+ etch_log_appender appenders[ETCH_LOG_MAX_APPENDER];
+};
+
+etch_status_t etch_log_register_appender(const char* name, etch_log_appender_desc* desc)
+{
+ uint16 i = 0;
+ for(i = 0; i < ETCH_LOG_MAX_APPENDER; i++) {
+ if(appenders[i].name == NULL) {
+ appenders[i].name = name;
+ appenders[i].desc = desc;
+ return ETCH_SUCCESS;
+ }
+ }
+ return ETCH_ERROR;
+}
+
+static etch_status_t etch_log_get_appender(const char* name, etch_log_appender_desc** desc)
+{
+ uint16 i = 0;
+
+ if(name == NULL || desc == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ for(i = 0; i < ETCH_LOG_MAX_APPENDER; i++) {
+ if(appenders[i].name != NULL && strcmp(appenders[i].name, name) == 0) {
+ *desc = appenders[i].desc;
+ return ETCH_SUCCESS;
+ }
+ }
+ return ETCH_ERROR;
+}
+
+
+etch_status_t etch_log_create(etch_log_t** logger, etch_config_t* config)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_log_t* newlogger = NULL;
+ char* propvalue = NULL;
+
+ if(logger == NULL || config == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ newlogger = etch_malloc(sizeof(etch_log_t), 0);
+ if(newlogger == NULL) {
+ return ETCH_ENOMEM;
+ }
+ memset(newlogger, 0, sizeof(etch_log_t));
+
+ status = etch_config_get_property_string(config, "etch.log", &propvalue);
+ if(propvalue != NULL) {
+ // parse log configuration
+ char* token = NULL;
+ char* buffer = NULL;
+ unsigned char i = 0;
+
+ buffer = etch_malloc(strlen(propvalue) + 1, 0);
+ strcpy(buffer, propvalue);
+
+ // read log level
+ token = strtok(buffer, ",");
+ if(strcmp(token, "xdebug") == 0) {
+ newlogger->level = ETCH_LOG_XDEBUG;
+ } else
+ if(strcmp(token, "debug") == 0) {
+ newlogger->level = ETCH_LOG_DEBUG;
+ } else
+ if(strcmp(token, "info") == 0) {
+ newlogger->level = ETCH_LOG_INFO;
+ } else
+ if(strcmp(token, "warn") == 0) {
+ newlogger->level = ETCH_LOG_WARN;
+ } else
+ if(strcmp(token, "error") == 0) {
+ newlogger->level = ETCH_LOG_ERROR;
+ } else {
+ newlogger->level = ETCH_LOG_WARN;
+ }
+
+ // read appenders
+ do {
+ token = strtok(NULL, ",");
+ token = strtrim(token);
+ if(token != NULL) {
+ etch_log_appender_desc* appender_desc = NULL;
+ status = etch_log_get_appender(token, &appender_desc);
+ if(status == ETCH_SUCCESS && i < ETCH_LOG_MAX_APPENDER) {
+ // add new appender
+ newlogger->appenders[i].desc = appender_desc;
+ newlogger->appenders[i].desc->create(&newlogger->appenders[i].data, config);
+ newlogger->appenders[i].desc->open(&newlogger->appenders[i].data);
+ i++;
+ } else {
+ // WARN appender not found or max appender count reached
+ }
+ }
+ } while(token != NULL);
+
+ // release buffer
+ etch_free(buffer);
+ }
+
+ *logger = newlogger;
+
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_log_log(etch_log_t* logger, const char* category, etch_log_level level, const char* file, int line, const char* fmt, ...)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ uint16 i = 0;
+ etch_log_message_t* logmsg = NULL;
+ va_list args;
+ size_t msg_length = 0;
+
+ status = etch_runtime_get_logger(&logger);
+ if(! logger || status != ETCH_SUCCESS) {
+ // error: no logger available
+ return ETCH_ERROR;
+ }
+
+ if(!(level >= logger->level)) {
+ return ETCH_SUCCESS;
+ }
+
+ // create message and log to registered appenders
+ logmsg = etch_malloc(sizeof(etch_log_message_t), 0);
+ ETCH_ASSERT(logmsg != NULL);
+ memset(logmsg, 0, sizeof(etch_log_message_t));
+
+ logmsg->category = category;
+ logmsg->level = level;
+ logmsg->file = file;
+ logmsg->line = line;
+ logmsg->threadid = etch_thread_current_id();
+ time(&logmsg->timestamp);
+ // format message
+ va_start(args, fmt);
+ msg_length = apr_vsnprintf(NULL, 0, fmt, args);
+ logmsg->message = etch_malloc(msg_length + 1, 0);
+ memset(logmsg->message, 0, msg_length + 1);
+ apr_vsnprintf(logmsg->message, msg_length + 1, fmt, args);
+ va_end(args);
+
+ // TODO: add message to log runner / separate thread
+ for(i = 0; i < ETCH_LOG_MAX_APPENDER; i++) {
+ if(logger->appenders[i].desc != NULL) {
+ logger->appenders[i].desc->log(&logger->appenders[i].data, logmsg);
+ }
+ }
+ etch_free(logmsg->message);
+ etch_free(logmsg);
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_log_logw(etch_log_t* logger, const char* category, etch_log_level level, const char* file, int line, const wchar_t* fmt, ...)
+{
+ //TODO: encode wchar_t to asci or utf-8 string
+ return ETCH_ENOTIMPL;
+}
+
+etch_status_t etch_log_destroy(etch_log_t* logger)
+{
+ uint16 i = 0;
+
+ if(logger != NULL) {
+ // free appenders
+ for(i = 0; i < ETCH_LOG_MAX_APPENDER; i++) {
+ if(logger->appenders[i].desc != NULL) {
+ logger->appenders[i].desc->close(&logger->appenders[i].data);
+ logger->appenders[i].desc->destroy(&logger->appenders[i].data);
+ logger->appenders[i].desc = NULL;
+ logger->appenders[i].data = NULL;
+ }
+ }
+ etch_free(logger);
+ }
+ return ETCH_SUCCESS;
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_map.c b/binding-c/runtime/c/src/main/common/etch_map.c
new file mode 100644
index 0000000..ee0de34
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_map.c
@@ -0,0 +1,495 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etchmap.c -- generic int or string to object map.
+ * note that this code does not implement an etch_map type, that type was added
+ * to etch after we had already defined this particular API.
+ * todo: convert global cache to use this code.
+ */
+
+#include "etch_map.h"
+#include "etch_mem.h"
+#include <wchar.h>
+
+#ifdef WIN32
+#pragma warning (disable:4996)
+#endif
+
+/*
+ * make_etchmap_key()
+ * given 32-bit key constructs alpha key for the map returning key byte length
+ */
+int make_etchmap_key(const unsigned int key, char* buf, const int buflen)
+{
+ //_itoa_s(key, buf, buflen, 10);
+ sprintf(buf, "%d", key);
+ return (int)strlen(buf);
+}
+
+
+/*
+ * etchmap_populate_out()
+ * populate caller's out struct
+ */
+void etchmap_populate_out(etch_hashitem* useritem, etch_hashitem* curritem)
+{
+ useritem->key = curritem->key;
+ useritem->value = curritem->value;
+ useritem->hash = curritem->hash;
+}
+
+
+/*
+ * etchmap_findxl()
+ * @return a reference to map content, not owned by caller.
+ * if the etch_hashitem out parameter is specified, it is populated on success.
+ */
+void* etchmap_findxl(etch_hashtable* map, char* key, unsigned keylen, void** out)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+ int result = jenkins_find(map->realtable, key, keylen, 0, (void**)&founditem);
+
+ #if ETCHMAP_DEBUG
+ #pragma warning(disable:4313)
+ if (result == 0)
+ printf("etchmap_found key %s len %d addr %08x\n",
+ key, keylen, founditem->value);
+ else printf("etchmap_notfound key %s len %d\n", key, keylen);
+ #endif
+
+ if (result == -1) return NULL;
+
+ if (out)
+ etchmap_populate_out(*out, founditem);
+
+ return founditem->value;
+}
+
+
+/*
+ * etchmap_find()
+ * locate object with specified key, returning it or NULL.
+ * @return a reference to map content, not owned by caller.
+ * if the etch_hashitem out parameter is specified, it is populated on success.
+ */
+void* etchmap_find(etch_hashtable* map, const unsigned int objkey, void** out)
+{
+ char ckey[ETCHMAP_MAX_IKEYSIZE+1];
+
+ unsigned keylen = make_etchmap_key(objkey, ckey, ETCHMAP_MAX_IKEYSIZE);
+
+ return etchmap_findxl(map, ckey, keylen, out);
+}
+
+
+/*
+ * etchmap_findx()
+ * locate object with specified ansi char key, returning it or NULL
+ */
+void* etchmap_findx(etch_hashtable* map, char* key, void** out)
+{
+ unsigned keylen = (unsigned)strlen(key);
+
+ return etchmap_findxl(map, key, keylen, out);
+}
+
+
+/*
+ * etchmap_findxw()
+ * locate object with specified unicode key, returning it or NULL
+ * @return a reference to map content, not owned by caller.
+ * if the etch_hashitem out parameter is specified, it is populated on success.
+ */
+void* etchmap_findxw(etch_hashtable* map, wchar_t* key, void** out)
+{
+ unsigned keylen = (unsigned)(wcslen(key) * sizeof(wchar_t));
+
+ return etchmap_findxl(map, (char*) key, keylen, out);
+}
+
+
+/*
+ * etchmap_find_by_hash()
+ * locate object with specified hashkey, returning it or NULL.
+ * @return a reference to map content, not owned by caller.
+ * if the etch_hashitem out parameter is specified, it is populated on success.
+ */
+void* etchmap_find_by_hash(etch_hashtable* map, const unsigned hash, void** out)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+int result = jenkins_findh(map->realtable, hash, map, (void**)&founditem);
+ if (result == -1) return NULL;
+
+ if (out)
+ etchmap_populate_out(*out, founditem);
+
+ return founditem->value;
+}
+
+
+/*
+ * etchmap_current()
+ * return object at current position
+ */
+etch_hashitem* etchmap_current(etch_hashtable* map)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+int result = jenkins_current(map->realtable, 0, (void**)&founditem);
+ return (result == -1)? NULL: founditem;
+}
+
+
+/*
+ * etchmap_delxl()
+ * remove specified object from map given ansi char key and length.
+ * return pointer to object, which is not freed here, it is owned by caller.
+ */
+void* etchmap_delxl (etch_hashtable* map, char* ckey, const unsigned keylen)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* founditem = &hashbucket;
+
+int result = jenkins_remove(map->realtable, ckey, keylen, 0, (void**)&founditem);
+ if (result == -1) return NULL;
+
+ free(founditem->key); /* free 4-byte key allocated in etchmap_add() */
+ return founditem->value;
+}
+
+
+/*
+ * etchmap_del()
+ * remove specified object from map given integer key.
+ * return pointer to object, which is not freed here, it is owned by caller.
+ */
+void* etchmap_del (etch_hashtable* map, const unsigned int objkey)
+{
+ char ckey[ETCHMAP_MAX_IKEYSIZE+1];
+
+ unsigned keylen = make_etchmap_key(objkey, ckey, ETCHMAP_MAX_IKEYSIZE);
+
+ return etchmap_delxl(map, ckey, keylen);
+}
+
+
+/*
+ * etchmap_delx()
+ * remove specified object from map given ansi char key.
+ * return pointer to object, which is not freed here, it is owned by caller.
+ */
+void* etchmap_delx (etch_hashtable* map, char* key)
+{
+ const unsigned keylen = (unsigned)strlen(key);
+
+ return etchmap_delxl(map, key, keylen);
+}
+
+
+/*
+ * etchmap_delxw()
+ * remove specified object from map given unicode key.
+ * return pointer to object, which is not freed here, it is owned by caller.
+ */
+void* etchmap_delxw (etch_hashtable* map, wchar_t* key)
+{
+ const unsigned keylen = (unsigned)(wcslen(key) * sizeof(wchar_t));
+
+ return etchmap_delxl(map, (char*) key, keylen);
+}
+
+
+/*
+ * etchmap_insertxl()
+ * add specified object to map given ansi char key and char length,
+ * with preexistence test optional.
+ * @param key a string for which caller retains ownership, map makes a copy.
+ * @param data an object owned by map or not depending on map attributes.
+ * @return hash of supplied key, or zero if insertion error.
+ */
+int etchmap_insertxl (etch_hashtable* map,
+ char* key, const unsigned keylen, void* data, const int is_check)
+{
+ int result = 0, keylent = 0;
+ char* pkey = NULL;
+ void* pval = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* inserteditem = &hashbucket;
+ memset(&hashbucket, 0, sizeof(etch_hashitem));
+
+ if (is_check)
+ { pval = etchmap_findxl(map, key, keylen, (void**)&inserteditem);
+ if (pval) return inserteditem->hash; /* entry exists */
+ }
+
+ keylent = keylen + 1;/* add new entry */
+ pkey = etch_malloc(keylent, 0);
+ //strcpy_s(pkey, keylent, key);
+ strcpy(pkey, key);
+
+ #if ETCHMAP_DEBUG
+ #pragma warning(disable:4313)
+ printf("etchmap_insertxl key %s len %d addr %08x\n", pkey, keylen, data);
+ #endif
+
+ result = jenkins_insert
+ (map->realtable, pkey, keylen, data, 0, 0, (void**)&inserteditem);
+
+ /* etchmap_dump(); */
+
+ return inserteditem->hash;
+}
+
+
+/*
+ * etchmap_insertxlw()
+ * add specified object to map given unicode key and char length,
+ * with preexistence test optional.
+ * @param key a string for which caller retains ownership, map makes a copy.
+ * @param data an object owned by map or not depending on map attributes.
+ * @return hash of supplied key, or zero if insertion error.
+ */
+int etchmap_insertxlw (etch_hashtable* map,
+ wchar_t* key, const unsigned keylen, void* data, const int is_check)
+{
+ int result = 0, keylent = 0;
+ wchar_t* pkey = NULL;
+ void* pval = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* inserteditem = &hashbucket;
+ memset(&hashbucket, 0, sizeof(etch_hashitem));
+
+ if (is_check)
+ { pval = etchmap_findxl(map, (char*) key, keylen, (void**)&inserteditem);
+ if (pval) return inserteditem->hash; /* entry exists */
+ }
+
+ keylent = keylen + sizeof(wchar_t); /* add new entry */
+ /* watch this spot. issue observed here where etch_malloc reports that it
+ * "could not add x to heap tracking store". my guess is that a heap item
+ * at address x had been etch_freed, but key x was for some reason not
+ * removed from the tracking table. memory address x was subsequently
+ * re-issued by malloc, and when etch_malloc attempts to add it to the
+ * tracking table, the address already exists there (or there is a hash
+ * collision?). regardless, this is a tracking error, is probably not a
+ * leak (because etch_free frees the memory regardless of this error),
+ * however the error messages are a problem so we need to address this.
+ */
+ pkey = etch_malloc(keylent, 0);
+ wcscpy(pkey, key);
+
+ result = jenkins_insert
+ (map->realtable, pkey, keylen, data, 0, 0, (void**)&inserteditem);
+
+ return inserteditem->hash;
+}
+
+
+/*
+ * etchmap_insert()
+ * add specified object to map with preexistence test optional.
+ * return inserted item hash, or zero if insertion error.
+ */
+int etchmap_insert (etch_hashtable* map,
+ const unsigned int key, void* data, const int is_check)
+{
+ char ckey[ETCHMAP_MAX_IKEYSIZE+1];
+ unsigned keylen = make_etchmap_key(key, ckey, ETCHMAP_MAX_IKEYSIZE);
+
+ return etchmap_insertxl(map, ckey, keylen, data, is_check);
+}
+
+
+/*
+ * etchmap_insertx()
+ * add specified object to map with preexistence test optional.
+ * param key a string for which caller retains ownership, map makes a copy.
+ * @param data the value of the key/value pair, owned by map or not depending
+ * on map attributes.
+ * return inserted item hash, or zero if insertion error.
+ */
+int etchmap_insertx (etch_hashtable* map, char* key, void* data, const int is_check)
+{
+ unsigned keylen = (unsigned)strlen(key);
+
+ return etchmap_insertxl(map, key, keylen, data, is_check);
+}
+
+
+/*
+ * etchmap_insertxw()
+ * add specified object to map with preexistence test optional.
+ * param key a string for which caller retains ownership, map makes a copy.
+ * @param data the value of the key/value pair, owned by map or not depending
+ * on map attributes.
+ * return inserted item hash, or zero if insertion error.
+ */
+int etchmap_insertxw (etch_hashtable* map, wchar_t* key, void* data, const int is_check)
+{
+ unsigned keylen = (unsigned)(wcslen(key) * sizeof(wchar_t));
+
+ return etchmap_insertxlw(map, key, keylen, data, is_check);
+}
+
+
+/*
+ * etchmap_add()
+ * add specified object to map given integer key.
+ * return 0 or -1.
+ */
+int etchmap_add (etch_hashtable* map, const unsigned int objkey, void* data)
+{
+ /* zero back from etchmap_insert() indicates insert error */
+ return etchmap_insert (map, objkey, data, TRUE)? 0: -1;
+}
+
+
+/*
+ * etchmap_addx()
+ * add specified object to map.
+ * return 0 or -1.
+ */
+int etchmap_addx(etch_hashtable* map, char* key, void* data)
+{
+ /* zero back from etchmap_insert() indicates insert error */
+ return etchmap_insertx (map, key, data, TRUE)? 0: -1;
+}
+
+
+/*
+ * etchmap_addxw()
+ * add specified object to map.
+ * return 0 or -1.
+ */
+int etchmap_addxw(etch_hashtable* map, wchar_t* key, void* data)
+{
+ /* zero back from etchmap_insert() indicates insert error */
+ return etchmap_insertxw (map, key, data, TRUE)? 0: -1;
+}
+
+
+/*
+ * etchmap_count()
+ * return count of items in map.
+ */
+int etchmap_count(etch_hashtable* map)
+{
+ return map? ((struct i_hashtable*)((etch_object*)map)->vtab)->count(map->realtable, 0, 0): 0;
+}
+
+
+/*
+ * string_to_etchobject_clear_handler()
+ * callback set to handle freeing of key memory during a clear() of
+ * a string to object type map, where the objects are etch_objects.
+ * handlers return FALSE to indicate memory free NOT handled.
+ */
+int string_to_etchobject_clear_handler (char* key, etch_object* value)
+{
+ etch_free(key);
+ etch_object_destroy(value);
+ return TRUE;
+}
+
+
+/*
+ * string_to_genericobject_clear_handler()
+ * callback set to handle freeing of key memory during a clear() of
+ * a string to object type map, where the objects are not etch_objects.
+ * handlers return FALSE to indicate memory free NOT handled.
+ */
+int string_to_genericobject_clear_handler (char* key, void* value)
+{
+ etch_free(key);
+ etch_free(value);
+ return TRUE;
+}
+
+
+/* - - - - - - - - - - - - - - - -
+ * etch_map (object to object map)
+ * - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchmap_is_object_key
+ * @return boolean value indicating whether hashtable key is an etch object
+ */
+int etchmap_is_object_key(etch_hashtable* map)
+{
+ int result = FALSE;
+ if (map) switch(map->content_type)
+ { case ETCHHASHTABLE_CONTENT_OBJECT_OBJECT:
+ case ETCHHASHTABLE_CONTENT_OBJECT_NONE:
+ result = TRUE;
+ }
+ return result;
+}
+
+
+/**
+ * etchmap_map_add
+ * inserts item to object/object hashtable
+ * it is assumed that hashkey has been pre-computed on the key object.
+ */
+int etchmap_map_add (etch_hashtable* map, etch_object* key, etch_object* value)
+{
+ int result = 0;
+ key->get_hashkey(key);
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->inserth(map->realtable, key, value, map, 0);
+ return result;
+}
+
+
+/**
+ * etchmap_map_find()
+ * finds object with specified object key .
+ * it is assumed that hashkey has been pre-computed on the key object.
+ * @return 0 or -1
+ * found item is returned via out parameter, if supplied.
+ */
+int etchmap_map_find(etch_hashtable* map, etch_object* key, etch_hashitem** out)
+{
+ const int result = ((struct i_hashtable*)((etch_object*)map)->vtab)->findh(map->realtable, key->get_hashkey(key), map, out);
+ return result;
+}
+
+
+/**
+ * etchmap_set_add
+ * inserts item to object/null hashtable
+ * it is assumed that hashkey has been pre-computed on the key object.
+ */
+int etchmap_set_add (etch_hashtable* set, etch_object* key)
+{
+ int result = 0;
+ key->get_hashkey(key);
+ result = ((struct i_hashtable*)((etch_object*)set)->vtab)->inserth(set->realtable, key, NULL, set, 0);
+ return result;
+}
+
+
+
+
+
diff --git a/binding-c/runtime/c/src/main/common/etch_mem.c b/binding-c/runtime/c/src/main/common/etch_mem.c
new file mode 100644
index 0000000..1b48b20
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_mem.c
@@ -0,0 +1,73 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etchmem.c -- heap memory allocate and free.
+ * the c binding wraps the heap allocator in order to track allocations.
+ * we supply the etch_malloc macro which, when ETCH_DEBUGALLOC is defined,
+ * will accept module name and code line number, along with object type and
+ * allocation byte length, in order to track allocations and frees, and thus
+ * enable identification of memory leaks.
+ */
+
+#include "etch_mem.h"
+#include "etch_log.h"
+
+static mallocFunc g_malloc = NULL;
+static freeFunc g_free = NULL;
+static reallocFunc g_realloc = NULL;
+
+void* _etch_malloc(size_t size, char* file, int line) {
+ void* p = NULL;
+
+ if (g_malloc == NULL)
+ p = malloc(size);
+ else
+ p = g_malloc(size);
+
+ //printf("malloc Mem: %p, File: %s, Line: %d\n", p, file, line);
+ //fflush(stdout);
+ return p;
+}
+
+void _etch_free(void* mem, char* file, int line) {
+ //printf("free Mem: %p, File: %s, Line: %d\n", mem, file, line);
+ //fflush(stdout);
+ if (g_free == NULL)
+ free(mem);
+ else
+ g_free(mem);
+}
+
+void* _etch_realloc(void* p, size_t size, char* file, int line) {
+ void* res = NULL;
+
+ if (g_realloc == NULL)
+ res = realloc(p, size);
+ else
+ res = g_realloc(p, size);
+
+ return res;
+}
+
+void etch_set_mallocator(mallocFunc myMallocFunc, freeFunc myFreeFunc, reallocFunc myReallocFunc)
+{
+ g_malloc = myMallocFunc;
+ g_free = myFreeFunc;
+ g_realloc = myReallocFunc;
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_mutex.c b/binding-c/runtime/c/src/main/common/etch_mutex.c
new file mode 100644
index 0000000..99c3d96
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_mutex.c
@@ -0,0 +1,207 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_mutex.c
+ * currently wraps apr mutex. our wrapper carries with it the benefit of
+ * tracking memory for our mutex, and thus for the underlying apr mutex.
+ */
+
+#include "etch_mutex.h"
+#include "etch_object.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+#include "etch_thread.h"
+
+static const char* LOG_CATEGORY = "etch_mutex";
+
+extern apr_pool_t* g_etch_main_pool;
+extern apr_thread_mutex_t* g_etch_main_pool_mutex;
+
+/**
+ * etchmutex: mutex object
+ */
+struct etch_mutex_t
+{
+ etch_object object;
+
+ void* impl;
+ int32 lockcount;
+};
+
+etch_status_t etch_mutex_create(etch_mutex_t** mutex, unsigned int flags, etch_pool_t* pool)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_mutex_t* newmutex = NULL;
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+
+ if(mutex == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ if(pool == NULL) {
+ pool = g_etch_main_pool;
+ }
+
+ if(flags != ETCH_MUTEX_NESTED && flags != ETCH_MUTEX_UNNESTED) {
+ return ETCH_EINVAL;
+ }
+
+ newmutex = (etch_mutex_t*)new_object(sizeof(etch_mutex_t), ETCHTYPEB_MUTEX, CLASSID_MUTEX);
+ ETCH_ASSERT(newmutex != NULL);
+
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ apr_status = apr_thread_mutex_create(&apr_mutex, flags, g_etch_main_pool);
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+ if(apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ etch_free(newmutex);
+ *mutex = NULL;
+ return ETCH_ERROR;
+ }
+ newmutex->impl = apr_mutex;
+ newmutex->lockcount = 0;
+ *mutex = newmutex;
+ return status;
+}
+
+etch_status_t etch_mutex_lock(etch_mutex_t* mutex)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+
+ if(mutex == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ apr_mutex = mutex->impl;
+ ETCH_ASSERT(apr_mutex);
+ apr_status = apr_thread_mutex_lock(apr_mutex);
+ switch(apr_status) {
+ case APR_SUCCESS:
+ mutex->lockcount++;
+ status = ETCH_SUCCESS;
+ break;
+ default: {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ status = ETCH_ERROR;
+ break;
+ }
+ }
+ return status;
+}
+
+etch_status_t etch_mutex_trylock(etch_mutex_t* mutex)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+
+ if(mutex == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ apr_mutex = mutex->impl;
+ ETCH_ASSERT(apr_mutex);
+ apr_status = apr_thread_mutex_trylock(apr_mutex);
+ switch(apr_status) {
+ case APR_SUCCESS:
+ mutex->lockcount++;
+ status = ETCH_SUCCESS;
+ break;
+ case APR_EBUSY:
+ status = ETCH_EBUSY;
+ break;
+ default: {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s\n", temp);
+ status = ETCH_EBUSY;
+ break;
+ }
+ }
+ return status;
+}
+
+etch_status_t etch_mutex_unlock(etch_mutex_t* mutex)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+
+ if(mutex == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ if(mutex->lockcount > 0) {
+ mutex->lockcount--;
+ }
+
+ apr_mutex = mutex->impl;
+ ETCH_ASSERT(apr_mutex);
+ apr_status = apr_thread_mutex_unlock(apr_mutex);
+ switch(apr_status) {
+ case APR_SUCCESS:
+ status = ETCH_SUCCESS;
+ break;
+ default: {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ status = ETCH_ERROR;
+ break;
+ }
+ }
+ return status;
+}
+
+etch_status_t etch_mutex_destroy(etch_mutex_t* mutex)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+
+ if(mutex == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ if(!is_etchobj_static_content(mutex)) {
+ apr_mutex = mutex->impl;
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ apr_status = apr_thread_mutex_destroy(apr_mutex);
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+ if(apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ status = ETCH_ERROR;
+ }
+ }
+
+ if (!is_etchobj_static_shell(mutex)) {
+ etch_free(mutex);
+ }
+ return status;
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_nativearray.c b/binding-c/runtime/c/src/main/common/etch_nativearray.c
new file mode 100644
index 0000000..2c78f52
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_nativearray.c
@@ -0,0 +1,553 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_nativearray.c
+ * etch_nativearray implementation
+ * does allocation and subscripting for n-dimensional arrays stored as a single
+ * byte vector, to overcome the well-known C compiler issues with passing around
+ * multi dimensioned arrays when the low order dimension is not known at compile
+ * time. the mapping here from byte vector to array tests out as k&r compliant,
+ * this is verified by constructing from a static array using new_etch_nativearray_from
+ * and testing that the get methods return the same result as statically indexed
+ * array items. as it stands, this code handles a max of 3 dimensions. it can be
+ * generalized to n dimensions, however the caller would have to allocate, populate,
+ * and pass arrays of subscripts for this to happen.
+ */
+
+#include <stdio.h>
+#include "etch_nativearray.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+
+etch_nativearray* init_etch_nativearray(unsigned short, const size_t, const int, const int, const int, const int);
+int etch_nativearray_put1 (etch_nativearray*, void*, int);
+int etch_nativearray_put2 (etch_nativearray*, void*, int, int);
+int etch_nativearray_put3 (etch_nativearray*, void*, int, int, int);
+int etch_nativearray_get1 (etch_nativearray*, void*, int);
+int etch_nativearray_get2 (etch_nativearray*, void*, int, int);
+int etch_nativearray_get3 (etch_nativearray*, void*, int, int, int);
+int etch_nativearray_set_content_type(etch_nativearray*);
+
+/*
+ * new_etch_nativearray()
+ * etch_nativearray constructor
+ * note that when creating arrays, the dimensions are passed low-order first,
+ * so for x[2][3][4], dim0 is 4, dim1 is 3, dim2 is 2. for y[7][9], dim0 is 9,
+ * dim1 is 7, and dim2 is zero of course.
+ */
+etch_nativearray* new_etch_nativearray(unsigned short class_id, const size_t itemsize,
+ const int numdims, const int dim0, const int dim1, const int dim2)
+{
+ etch_nativearray* newarray = init_etch_nativearray(class_id, itemsize, numdims, dim0, dim1, dim2);
+ if (NULL == newarray) return NULL;
+
+ newarray->values = etch_malloc(newarray->bytecount, ETCHTYPEB_BYTES);
+ memset(newarray->values, 0, newarray->bytecount);
+ newarray->is_content_owned = TRUE;
+
+ etch_nativearray_set_content_type(newarray);
+ return newarray;
+}
+
+/*
+ * new_etch_nativearray_from()
+ * etch_nativearray constructor from existing byte vector
+ * @param values source array, caller retains ownership.
+ */
+etch_nativearray* new_etch_nativearray_from(void* values, unsigned short class_id, const size_t itemsize,
+ const int numdims, const int dim0, const int dim1, const int dim2)
+{
+ etch_nativearray* newarray = init_etch_nativearray(class_id, itemsize, numdims, dim0, dim1, dim2);
+ if (NULL == newarray) return NULL;
+
+ newarray->values = values;
+ newarray->is_content_owned = FALSE;
+
+ etch_nativearray_set_content_type(newarray);
+ return newarray;
+}
+
+/*
+ * new_etch_nativearray_of()
+ * etch_nativearray constructor for array of objects of specified type and class
+ */
+etch_nativearray* new_etch_nativearray_of(unsigned short content_obj_type, unsigned short content_class_id, const int numdims, const int dim0, const int dim1, const int dim2)
+{
+ etch_nativearray* newarray = init_etch_nativearray(CLASSID_ARRAY_OBJECT, sizeof(void*), numdims, dim0, dim1, dim2);
+ if (NULL == newarray) return NULL;
+
+ newarray->content_obj_type = content_obj_type;
+ newarray->content_class_id = content_class_id;
+ return newarray;
+}
+
+/*
+ * init_etch_nativearray()
+ * etch_nativearray private constructor
+ * assumes max number of dimensions is 3
+ */
+etch_nativearray* init_etch_nativearray(unsigned short class_id, const size_t itemsize,
+ const int numdims, const int dim0, const int dim1, const int dim2)
+{
+ etch_nativearray* newarray = NULL;
+ int i,j;
+ if (numdims < 1 || itemsize < 1) return NULL;
+ if (numdims > 1 && dim1 < 1) return NULL;
+ if (numdims > 2 && dim2 < 1) return NULL;
+
+ newarray = (etch_nativearray*)new_object(sizeof(etch_nativearray), ETCHTYPEB_NATIVEARRAY, class_id);
+
+ newarray->numdims = numdims;
+ newarray->itemsize = itemsize;
+
+ /* dimension array contains the array dimensions,
+ * e.g., for x[2][3][4], [0] is 4, [1] is 3, [2] is 2
+ */
+ newarray->dimension[0] = dim0;
+ newarray->dimension[1] = numdims > 1? dim1: 0;
+ newarray->dimension[2] = numdims > 2? dim2: 0;
+
+ /* dimsize array contains size in bytes of the next lower dimension,
+ * stored so that the values need not be calculated on each array access.
+ * [0] is item size, so for int x[5][7][9], [0] is sizeof(int),
+ * [1] is 9*sizeof(int), [2] is 7*9*sizeof(int)
+ */
+ newarray->dimsize[0] = itemsize;
+ newarray->bytecount = itemsize * dim0;
+
+ for(i = 1, j = 0; i < numdims; i++, j++)
+ {
+ newarray->dimsize[i] = newarray->dimension[j] * newarray->dimsize[j];
+ newarray->bytecount *= newarray->dimension[i];
+ }
+
+ ((etch_object*)newarray)->clone = etch_nativearray_clone;
+ ((etch_object*)newarray)->destroy = destroy_etch_nativearray;
+
+ newarray->put1 = etch_nativearray_put1;
+ newarray->put2 = etch_nativearray_put2;
+ newarray->put3 = etch_nativearray_put3;
+ newarray->get1 = etch_nativearray_get1;
+ newarray->get2 = etch_nativearray_get2;
+ newarray->get3 = etch_nativearray_get3;
+
+ return newarray;
+}
+
+
+
+void* etch_nativearray_clone(void* obj)
+{
+ etch_nativearray* source = (etch_nativearray*)obj;
+ etch_nativearray* newarray = NULL;
+
+ if(source == NULL) {
+ return NULL;
+ }
+
+ newarray = new_etch_nativearray_from(source->values, source->content_class_id, source->itemsize, source->numdims, (int)source->dimension[0], (int)source->dimension[1], (int)source->dimension[2]);
+
+
+ // always own copies of unwrapped content
+ if(source->content_class_id == CLASSID_UNWRAPPED){
+ newarray->values = etch_malloc(source->bytecount, ETCHTYPEB_BYTES);
+ memcpy(newarray->values,source->values,source->bytecount);
+ newarray->is_content_owned = TRUE;
+ }else {
+ //never own anything else
+ newarray->is_content_owned = FALSE;
+ newarray->values = source->values;
+ }
+ newarray->content_class_id = source->content_class_id;
+ newarray->content_obj_type = source->content_obj_type;
+ ((etch_object*)newarray)->class_id = ((etch_object*)source)->class_id;
+ return newarray;
+
+}
+
+/**
+ * destroy_etch_nativearray_content()
+ * destroy nativearray's content vector.
+ * return -1 if content not owned by nativearray and therefore not destroyed;
+ * otherwise return zero, regardless of whether content was null.
+ */
+int destroy_etch_nativearray_content(etch_nativearray* a)
+{
+ int result = 0;
+ if (!is_etchobj_static_content(a) && a->is_content_owned) {
+ if(a->content_class_id != CLASSID_UNWRAPPED){
+ size_t i = 0;
+ size_t length = a->bytecount / sizeof(etch_object*);
+ ETCH_ASSERT(a->bytecount % sizeof(etch_object*) == 0);
+ for(; i < length; i++){
+ void** temp = a->values;
+ if( temp[i] ){
+ etch_object_destroy(temp[i]);
+ }
+ }
+ }
+ etch_free(a->values); /* OK if values null */
+ }
+
+ else result = -1;
+ return result;
+}
+
+
+/**
+ * destroy_etch_nativearray()
+ * etch_nativearray destructor
+ */
+int destroy_etch_nativearray(void* obj)
+{
+ etch_nativearray* a = (etch_nativearray*)obj;
+ if (a->is_content_owned && !is_etchobj_static_content(a))
+ destroy_etch_nativearray_content(a);
+ destroy_objectex((etch_object*)a);
+ return 0;
+}
+
+
+/*
+ * new_subarray()
+ * construct and return subaarray[i] of this array.
+ * subarray points into parent content and so does not own its content
+ */
+etch_nativearray* new_subarray(etch_nativearray* a, const int i)
+{
+ etch_nativearray* subarray = NULL;
+ const int sub_numdims = a->numdims - 1;
+
+ if (a->numdims > 1 && i < (int) a->dimension[sub_numdims])
+ {
+ const size_t sub_bytelen = a->dimsize[sub_numdims];
+ const size_t vector_offset = i * sub_bytelen;
+ byte* subvector = (byte*) a->values + vector_offset;
+
+ subarray = new_etch_nativearray_from(subvector,
+ ((etch_object*)a)->class_id, (int) a->itemsize, sub_numdims,
+ (int) a->dimension[0], (int) a->dimension[1], 0);
+
+ subarray->bytecount = sub_bytelen;
+ subarray->content_obj_type = a->content_obj_type;
+ subarray->content_class_id = a->content_class_id;
+ subarray->is_content_owned = a->is_content_owned;
+
+ subarray->counts[0] = a->counts[0]; /* user-maintained counts */
+ subarray->counts[1] = a->counts[1];
+ subarray->counts[2] = 0;
+ }
+
+ return subarray;
+}
+
+
+/*
+ * etch_nativearray_get_element()
+ * return element[i] of specified array, which, if array is multi-dimensioned,
+ * will be another native array, otherwise an etch object. for example, if
+ * the supplied array is byte[2][4], a native array of byte[4] is returned.
+ * if the supplied array is byte[4], an etch_byte object is returned.
+ * returned subarrays do not own content, their content references the content
+ * of their parent.
+ */
+etch_object* etch_nativearray_get_element(etch_nativearray* a, const int i)
+{
+ int result = 0;
+ etch_object* wrapped_element = NULL;
+
+ const int sub_numdims = a->numdims - 1;
+ if (sub_numdims < 0) return NULL;
+
+ if (sub_numdims > 0)
+ return (etch_object*) new_subarray(a, i);
+
+ result = etch_nativearray_get_wrapped_component(a, i, &wrapped_element);
+
+ return wrapped_element;
+}
+
+
+/*
+ * etch_nativearray_set_content_type()
+ * set content_obj_type and content_class_id based on array class_id.
+ * returns zero if content type was set, otherwise -1
+ */
+int etch_nativearray_set_content_type(etch_nativearray* a)
+{
+ unsigned short obj_type = 0;
+ int result = 0;
+
+ switch(((etch_object*)a)->class_id)
+ {
+ case CLASSID_ARRAY_BYTE: obj_type = ETCHTYPEB_BYTE; break;
+ case CLASSID_ARRAY_BOOL: obj_type = ETCHTYPEB_BOOL; break;
+ case CLASSID_ARRAY_INT8: obj_type = ETCHTYPEB_INT8; break;
+ case CLASSID_ARRAY_INT16: obj_type = ETCHTYPEB_INT16; break;
+ case CLASSID_ARRAY_INT32: obj_type = ETCHTYPEB_INT32; break;
+ case CLASSID_ARRAY_INT64: obj_type = ETCHTYPEB_INT64; break;
+ case CLASSID_ARRAY_FLOAT: obj_type = ETCHTYPEB_IEEE32; break;
+ case CLASSID_ARRAY_DOUBLE: obj_type = ETCHTYPEB_IEEE64; break;
+ }
+
+ if (obj_type) {
+ a->content_obj_type = obj_type;
+ a->content_class_id = CLASSID_UNWRAPPED;
+ }
+ else switch(((etch_object*)a)->class_id)
+ {
+ case CLASSID_ARRAY_OBJECT:
+ a->content_obj_type = ETCHTYPEB_ETCHOBJECT;
+ a->content_class_id = CLASSID_OBJECT;
+ break;
+
+ case CLASSID_ARRAY_STRING:
+ a->content_obj_type = ETCHTYPEB_STRING;
+ a->content_class_id = CLASSID_STRING;
+ break;
+
+ default:
+ result = -1; /* indicates content type not set */
+ }
+
+ return result;
+}
+
+
+/**
+ * put1()
+ * insert item to singly dimensioned array
+ * array access can be generalized by passing an array of subscripts; however
+ * since we are currently handling max 3 dimensions, the calling sequence is
+ * much simpler when we have separate accessors for each dimensionality.
+ */
+int etch_nativearray_put1(etch_nativearray* a, void* value, int i)
+{
+ size_t offset = etch_nativearray_off1(a, i);
+ if (value == NULL || offset < 0) return -1;
+ memcpy((byte*)a->values + offset, value, a->itemsize);
+ return 0;
+}
+
+
+/**
+ * put2()
+ * insert item to 2-dimensional array
+ */
+int etch_nativearray_put2(etch_nativearray* a, void* value, int i, int j)
+{
+ size_t offset = etch_nativearray_off2(a, i, j);
+ if (value == NULL || offset < 0) return -1;
+ memcpy((byte*)a->values + offset, value, a->itemsize);
+ return 0;
+}
+
+
+/**
+ * put3()
+ * insert item to 3-dimensional array
+ */
+int etch_nativearray_put3(etch_nativearray* a, void* value, int i, int j, int k)
+{
+ size_t offset = etch_nativearray_off3(a, i, j, k);
+ if (value == NULL || offset < 0) return -1;
+ memcpy((byte*)a->values + offset, value, a->itemsize);
+ return 0;
+}
+
+
+/**
+ * get1()
+ * access and return item from singly dimensioned array
+ */
+int etch_nativearray_get1(etch_nativearray* a, void* value, int i)
+{
+ size_t offset = etch_nativearray_off1(a, i);
+ if (value == NULL || offset < 0) return -1;
+ memcpy(value, (byte*)a->values + offset, a->itemsize);
+ return 0;
+}
+
+
+/**
+ * get2()
+ * access and return item from 2-dimensional array
+ */
+int etch_nativearray_get2(etch_nativearray* a, void* value, int i, int j)
+{
+ size_t offset = etch_nativearray_off2(a, i, j);
+ if (value == NULL || offset < 0) return -1;
+ memcpy(value, (byte*)a->values + offset, a->itemsize);
+ return 0;
+}
+
+
+/**
+ * get3()
+ * access and return item from 3-dimensional array
+ */
+int etch_nativearray_get3(etch_nativearray* a, void* value, int i, int j, int k)
+{
+ size_t offset = etch_nativearray_off3(a, i, j, k);
+ if (value == NULL || offset < 0) return -1;
+ memcpy(value, (byte*)a->values + offset, a->itemsize);
+ return 0;
+}
+
+
+/*
+ * etch_nativearray_off1()
+ * calculate byte offset into byte vector for a 1-dimension array
+ */
+size_t etch_nativearray_off1(etch_nativearray* a, int i)
+{
+ size_t offset = 0;
+ if (i >= (int) a->dimension[0] || i < 0) return -1;
+
+ offset = i * a->dimsize[0];
+ return offset;
+}
+
+
+/*
+ * etch_nativearray_off2()
+ * calculate byte offset into byte vector for a 2-dimension array
+ */
+size_t etch_nativearray_off2(etch_nativearray* a, int i, int j)
+{
+ size_t offset = 0;
+ if ((i >= (int) a->dimension[1] || i < 0)
+ || (j >= (int) a->dimension[0] || j < 0)) return -1;
+
+ offset = (i * a->dimsize[1]) + (j * a->dimsize[0]);
+ return offset;
+}
+
+
+/*
+ * etch_nativearray_off3()
+ * calculate byte offset into byte vector for a 3-dimension array
+ */
+size_t etch_nativearray_off3(etch_nativearray* a, int i, int j, int k)
+{
+ size_t offset = 0;
+ if ((i >= (int) a->dimension[2] || i < 0)
+ || (j >= (int) a->dimension[1] || j < 0)
+ || (k >= (int) a->dimension[0] || k < 0)) return -1;
+
+ offset = (i * a->dimsize[2]) + (j * a->dimsize[1]) + (k * a->dimsize[0]);
+ return offset;
+}
+
+
+
+
+/**
+ * etch_nativearray_get_wrapped_component()
+ * get wrapped component[i] from the single-dimensioned native array.
+ * caller owns returned object.
+ * todo: test that if array content was wrapped, the is_value_owned
+ * setting is sufficient for tranparent memory management of returned object.
+ */
+int etch_nativearray_get_wrapped_component(etch_nativearray* a, const int i, etch_object** out)
+{
+ const int numentries = (int) a->dimension[0];
+ const int is_wrapped_content = a->content_class_id != 0;
+ int result = 0;
+ union
+ { char vbyte; short vint16; int vint32; int64 vint64;
+ float vfloat;double vdouble;void* vaddr; int64 all;
+ } v;
+
+ if (a->numdims != 1 || i >= numentries) return -1;
+ v.all = 0;
+
+ if (is_wrapped_content)
+ { /* content of the native array is wrapped, e.g, etch_object*,
+ * so we return the item as is */
+ result = a->get1(a, &v.vaddr, i);
+ *out = v.vaddr;
+ }
+ else
+ {
+
+
+ switch(a->content_obj_type)
+ { /* content of the native array is unwrapped, e.g, int[], so we wrap
+ * the array item with an etch object prior to returning it */
+ case ETCHTYPEB_BYTE:
+ result = a->get1(a, &v.vbyte, i);
+ *out = (etch_object*) new_byte(v.vbyte);
+ break;
+
+ case ETCHTYPEB_BOOL:
+ result = a->get1(a, &v.vbyte, i);
+ *out = (etch_object*) new_boolean(v.vbyte);
+ break;
+
+ case ETCHTYPEB_INT8:
+ result = a->get1(a, &v.vbyte, i);
+ *out = (etch_object*) new_int8(v.vbyte);
+ break;
+
+ case ETCHTYPEB_INT16:
+ result = a->get1(a, &v.vint16, i);
+ *out = (etch_object*) new_int16(v.vint16);
+ break;
+
+ case ETCHTYPEB_INT32:
+ result = a->get1(a, &v.vint32, i);
+ *out = (etch_object*) new_int32(v.vint32);
+ break;
+
+ case ETCHTYPEB_INT64:
+ result = a->get1(a, &v.vint64, i);
+ *out = (etch_object*) new_int64(v.vint64);
+ break;
+
+ case ETCHTYPEB_IEEE32:
+ result = a->get1(a, &v.vfloat, i);
+ *out = (etch_object*) new_float(v.vfloat);
+ break;
+
+ case ETCHTYPEB_IEEE64:
+ result = a->get1(a, &v.vdouble, i);
+ *out = (etch_object*) new_double(v.vdouble);
+ break;
+
+ case ETCHTYPEB_STRING:
+ result = a->get1(a, &v.vaddr, i);
+ *out = (etch_object*) new_stringw(v.vaddr);
+ printf("warning\n");
+ break;
+
+ default: /* item is an anonymous pointer to a value owned by caller */
+ {
+ result = a->get1(a, &v.vaddr, i);
+ *out = (etch_object*) v.vaddr;
+ }
+ }
+ /*
+ if(out && *out && ! a->is_content_owned)
+ set_etchobj_static_content((*out));
+ */
+ } /* switch(a->content_obj_type) */
+
+ return result;
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_object.c b/binding-c/runtime/c/src/main/common/etch_object.c
new file mode 100644
index 0000000..9f4b269
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_object.c
@@ -0,0 +1,1102 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_object.c
+ */
+
+#include "etch_cache.h"
+#include "etch_exception.h"
+#include "etch_nativearray.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+#include <wchar.h>
+
+
+unsigned int primitive_objsize[] =
+{
+ sizeof(etch_byte), sizeof(etch_boolean), sizeof(etch_int8), sizeof(etch_int16),
+ sizeof(etch_int32), sizeof(etch_int64), sizeof(etch_float), sizeof(etch_double),
+ sizeof(etch_string),sizeof(etch_date),
+};
+
+/**
+ * destroy_object()
+ * default virtual destructor for an etch_object* based object
+ * (other than etch_object), invoked by all other object dtors.
+ */
+int destroy_object(void* thisx)
+{
+ etch_object* thisobj = (etch_object*)thisx;
+ etch_object* parentobj = NULL;
+ if (thisobj == NULL) return -1;
+ parentobj = thisobj->parent;
+
+ if (thisobj->result)
+ {
+ etch_free(thisobj->result);
+ }
+
+ if (!is_etchobj_static_shell(thisobj))
+ etch_free(thisobj);
+
+ etch_object_destroy(parentobj);
+
+ return 0;
+}
+
+/**
+ * destroy_objectex()
+ * mark object as "refcount already decremented" and invoke destroy_object
+ */
+int destroy_objectex(etch_object* thisobj)
+{
+ return destroy_object(thisobj);
+}
+
+etch_status_t etch_object_destroy(void* pobject)
+{
+ if(pobject){
+ ((etch_object*)pobject)->destroy((etch_object*)pobject);
+ }
+ return ETCH_SUCCESS;
+}
+
+
+etch_object* etch_object_clone_func(void* pobject)
+{
+ etch_object* object = (etch_object*)pobject;
+ if(object != NULL) {
+ return object->clone(object);
+ }
+ return NULL;
+}
+
+
+/**
+ * set_etch_assignable_arg_from()
+ * populate an argument to etchobj_is_assignable_from() from specified object
+ */
+void set_etch_assignable_arg_from(etch_objclass* arg, etch_object* obj)
+{
+ vtabmask* vtab = NULL;
+ memset(arg, 0, sizeof(etch_objclass));
+ if (NULL == obj) return;
+
+ ((etch_objclass*)arg)->obj_type = obj->obj_type;
+ ((etch_objclass*)arg)->class_id = obj->class_id;
+ arg->parent = obj->parent;
+
+ if (vtab = (vtabmask*) ((etch_object*)obj)->vtab)
+ {
+ arg->vtable_class_id = ((etch_object*)vtab)->class_id;
+ arg->inherits_from = vtab->inherits_from;
+ }
+
+ switch(((etch_object*)obj)->obj_type)
+ {
+ case ETCHTYPEB_NATIVEARRAY:
+ arg->numdims = ((etch_nativearray*)obj)->numdims;
+ arg->content_obj_type = ((etch_nativearray*)obj)->content_obj_type;
+ arg->content_class_id = ((etch_nativearray*)obj)->content_class_id;
+ break;
+ case ETCHTYPEB_ARRAYVAL:
+ arg->numdims = ((etch_collection_mask*)obj)->n;
+ arg->content_obj_type = ((etch_collection_mask*)obj)->content_obj_type;
+ arg->content_class_id = ((etch_collection_mask*)obj)->content_class_id;
+ break;
+ case ETCHTYPEB_ARRAYLIST:
+ arg->numdims = 1;
+ arg->content_obj_type = ((etch_collection_mask*)obj)->content_obj_type;
+ arg->content_class_id = ((etch_collection_mask*)obj)->content_class_id;
+ }
+}
+
+/**
+ * etchobj_is_assignable_from()
+ * determines if the class of thisobj is the same as, or is a superclass of,
+ * the class specified by that_class_id. tests whether the type represented
+ * by that_obj_type and that_class_id can be converted to the type of thisobj
+ * via an identity conversion or via a widening reference conversion.
+ *
+ * @param target the class of left side of the assignment (to).
+ * during validation, this is the class of object the validator expects
+ * to validate. if target object is an array, this is the array content class.
+
+ * @param source the class of right side of the assignment (from).
+ * during validation, this is the class of the object being validated.
+ * if target object is an array, this is the array content class.
+ */
+int etchobj_is_assignable_from(etch_objclass* target, etch_objclass* source)
+{
+ if (((etch_objclass*)source)->class_id == target->class_id && source->numdims == 0)
+ return TRUE; /* identity case */
+
+ /* if left side is Object wrapper (not Object[]), anything can be assigned
+ * to it, since the c binding does not receive anything that is unwrapped.
+ * i.e. in java Object.class is not assignable from int.class; however here
+ * we don't have an int.class, only wrapped integers. if this were not the
+ * case this test would come after the etch primitives test, since a class
+ * not derived from object can't be assigned to object.
+ */
+ if (is_etch_object_type(((etch_objclass*)target)->obj_type, ((etch_objclass*)target)->class_id)) return TRUE;
+
+ if (((etch_objclass*)target)->obj_type == ETCHTYPEB_PRIMITIVE
+ || ((etch_objclass*)source)->obj_type == ETCHTYPEB_PRIMITIVE) return FALSE;
+
+ /* if left (target) side is an array, and right (source) side is an
+ * array of the same dimensions, and either of the same content type,
+ * or an array of Object, then it is assignable.
+ * this code is not robust - need to rethink assignability for default
+ * array validator with various validation object array types.
+ * currently we are validating very loosely for array types. we need
+ * a more general means of validating arrays, i.e. common attributes
+ * among array types (native, value, list)
+ */
+ if (is_etch_objarray_type (((etch_objclass*)target)->obj_type, ((etch_objclass*)target)->class_id)
+ || is_etch_arraylist_type (((etch_objclass*)target)->obj_type, ((etch_objclass*)target)->class_id)
+ || is_etch_nativearray_type(((etch_objclass*)target)->obj_type, ((etch_objclass*)target)->class_id))
+ {
+ if (target->numdims == source->numdims)
+ {
+ /* this line added to pass anything using default array validator */
+ if (target->content_obj_type == 0 && target->content_class_id == 0)
+ return TRUE;
+
+ if (target->content_class_id == CLASSID_UNWRAPPED)
+ return target->content_obj_type == source->content_obj_type;
+
+ if (target->content_class_id == CLASSID_OBJECT
+ || target->content_class_id == source->content_class_id)
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ else /* if source inherits from target, source can be assigned to target */
+ if (source->parent)
+ { /* inheritance model 2 - inherited objects are instantiated and chained */
+ etch_object* thisparent = source->parent;
+
+ while(thisparent) /* walk source object's inheritance chain */
+ {
+ if (((etch_object*)thisparent)->class_id == target->class_id) return TRUE;
+ thisparent = thisparent->parent;
+ }
+ }
+ else
+ { int ndx = 0;
+ etchparentinfo* parentinfo;
+ /* inheritance model 1 - inherited data flattened into single object */
+
+ while(1) /* iterate source object's inheritance list */
+ {
+ if (NULL == (parentinfo = get_next_etch_parentex
+ (((etch_objclass*)source)->class_id, source->inherits_from, ndx++)))
+ break;
+
+ if (((etch_objclass*)parentinfo)->class_id == target->class_id)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * etchobj_is_assignable_from_ex()
+ * see comments for etchobj_is_assignable_from()
+ */
+int etchobj_is_assignable_fromobj(etch_object* targetobj, etch_object* sourceobj)
+{
+ etch_objclass targetarg, sourcearg;
+ set_etch_assignable_arg_from(&targetarg, targetobj);
+ set_etch_assignable_arg_from(&sourcearg, sourceobj);
+ return etchobj_is_assignable_from(&targetarg, &sourcearg);
+}
+
+
+
+/**
+ * destroy_string()
+ */
+int destroy_string(void* data)
+{
+ etch_string* thisp = data;
+ if (!is_etchobj_static_content(thisp))
+ etch_free(thisp->v.value); /* OK if null */
+ destroy_objectex((etch_object*)thisp);
+ return 0;
+}
+
+/**
+ * default virtual copy constructor for etch object.
+ */
+void* clone_object(void* thisx)
+{
+ etch_object* pthis = (etch_object*)thisx;
+ void* pnew = NULL;
+ unsigned objsize = pthis->length;
+ if (objsize < sizeof(etch_object)) objsize = sizeof(etch_object);
+
+ pnew = etch_malloc (objsize, ((etch_object*)pthis)->obj_type);
+ memcpy(pnew, pthis, objsize);
+
+ return pnew;
+}
+
+/**
+ * default virtual copy constructor for objects requiring deep copy
+ */
+void* clone_null(void* pthis)
+{
+ return NULL;
+}
+
+/**
+ * clone_string()
+ */
+void* clone_string(void* obj)
+{
+ etch_string* thisp = (etch_string*)obj;
+ etch_string* newobj = (etch_string*) clone_object((etch_object*)thisp);
+
+ newobj->v.value = etch_malloc(thisp->byte_count, ETCHTYPEB_STRING);
+ memcpy(newobj->v.value, thisp->v.value, thisp->byte_count);
+ newobj->byte_count = thisp->byte_count;
+ newobj->char_count = thisp->char_count;
+
+ return newobj;
+}
+
+/**
+ * new_etchresult()
+ * generate etchobject result object
+ */
+etchresult* new_etchresult(const int result, const int reason)
+{
+ etchresult* newresult = etch_malloc(sizeof(etchresult), ETCHTYPEB_RESULT);
+ memset(newresult, 0, sizeof(etchresult));
+ newresult->resultcode = result;
+ newresult->reasoncode = reason;
+ return newresult;
+}
+
+/**
+ * defgethashkey
+ * default hashkey computation for an etch object
+ */
+uint32 defgethashkey(void* data)
+{
+ etch_object* obj = (etch_object*)data;
+ void* hashitem = obj; /* uses the object address as hash source */
+ if (NULL == hashitem) return 0;
+ if(obj->hashkey == 0){
+ obj->hashkey = etchhash((char*)&hashitem, sizeof(void*), 0);
+ }
+ return obj->hashkey;
+}
+
+/**
+ * new_object()
+ * basic object constructor
+ * sets type, class, size, default destructor, and copy constructor.
+ */
+etch_object* new_object(const int objsize, const unsigned short obj_type, const unsigned short class_id)
+{
+ etch_object* newobj = 0;
+
+ newobj = etch_malloc(objsize, obj_type);
+
+ memset(newobj, 0, objsize);
+ ((etch_object*)newobj)->obj_type = obj_type;
+ ((etch_object*)newobj)->class_id = class_id;
+ newobj->length = objsize;
+ newobj->destroy = destroy_object;
+ newobj->clone = clone_object;
+ newobj->get_hashkey = defgethashkey;
+ //newobj->get_hashkey(newobj);
+ return newobj;
+}
+
+/**
+ * short_type()
+ * return a 16-bit type code of 2 8-bit parts
+ */
+short short_type(unsigned i, unsigned j)
+{
+ return (short) ( ( ((byte)i) << 16 ) | ((byte)j) );
+}
+
+
+
+/**
+ * verify_object()
+ * verify that the object passed is of the specified type and class.
+ * zero is a match for either so pass zero to not validate either.
+ */
+int verify_object(etch_object* obj, const unsigned short type, const unsigned short id, void** out)
+{
+ if (obj == NULL) return -1;
+ if (type != 0 && ((etch_object*)obj)->obj_type != type) return -1;
+ if (id != 0 && ((etch_object*)obj)->class_id != id) return -1;
+ return 0;
+}
+
+/**
+ * get_base_vtable()
+ * walks a vtable chain returning the final vtab in the chain
+ */
+void* get_base_vtable(etch_object* obj)
+{
+ vtabmask* basevtab = ((etch_object*)obj)->vtab;
+ while (basevtab && ((etch_object*)basevtab)->vtab)
+ basevtab = ((etch_object*)basevtab)->vtab;
+ return basevtab;
+}
+
+/**
+ * destroy_vtable()
+ */
+int destroy_vtable(void* data)
+{
+ etch_object* vtab = (etch_object*)data;
+ etchparentinfo* inheritlist = ((vtabmask*)vtab)->inherits_from;
+
+ if (inheritlist && !is_etchobj_static_content(vtab))
+ {
+ free(inheritlist); /* vtables not tracked */
+ ((vtabmask*)vtab)->inherits_from = NULL;
+ }
+
+ if (!is_etchobj_static_shell(vtab))
+ free(vtab); /* vtables not tracked */
+
+ return 0;
+}
+
+/**
+ * get_class_cachekey()
+ * get the unique value used for keying the indicated class in a class cache.
+ */
+unsigned get_class_cachekey(unsigned short obj_type, unsigned short class_id)
+{
+ unsigned key = (obj_type << 16) | class_id;
+ return etchhash(&key, 4, 0);
+}
+
+/**
+ * get_vtable_cachehkey()
+ * calculates ad returns the *cache* key for specified vtable object
+ * the hashkey on the vtable object is not an object key, it is a class key,
+ * since vtables are cached by class.
+ */
+unsigned get_vtable_cachehkey(unsigned short class_id)
+{
+ return get_class_cachekey(ETCHTYPEB_VTABLE, class_id);
+}
+
+ /**
+ * get_vtabobj_hashkey()
+ * sets and gets the *cache* key for specified vtable object
+ * the hashkey on the vtable object is not an object key, it is a class key,
+ * since vtables are cached by class.
+ */
+uint32 get_vtabobj_hashkey(void* data)
+{
+ etch_object* vtabobj = (etch_object*)data;
+ return vtabobj->hashkey = get_vtable_cachehkey(((etch_object*)vtabobj)->class_id);
+}
+
+/**
+ * new_vtable()
+ * instantiate a new virtual function table of the specified type,
+ * defaulting all methods to those of specified parent if requested
+ */
+void* new_vtable(const void* parentvtab, const size_t size, const short classid)
+{
+ etch_object* newvtab = etch_malloc(size, 0); /* vtable memory is not tracked */
+
+ if(parentvtab)
+ memcpy(newvtab, parentvtab, size);
+ else
+ memset(newvtab, 0, size);
+
+ ((etch_object*)newvtab)->obj_type = ETCHTYPEB_VTABLE;
+ ((etch_object*)newvtab)->class_id = classid;
+ newvtab->length = (unsigned) size;
+ newvtab->destroy = destroy_vtable;
+ newvtab->clone = clone_object;
+
+ newvtab->get_hashkey = get_vtabobj_hashkey;
+ //newvtab->get_hashkey(newvtab);
+
+ return newvtab;
+}
+
+
+/**
+ * get_vtab_inheritance_list()
+ * add an inheritance list to the specified object, or fetch existing list.
+ * an inheritance list exists in the vtable since we don't need to duplicate it
+ * for every instance. if there is no vtable in the specified object, a place-
+ * holder vtable is instantiated in the object. recall that vtables are freed
+ * when the global cache is cleared. the first entry in an inheritance list
+ * contains the list attributes, therefore the list is one-based.
+ * if an appropriately-sized list is already cached, the cached list is
+ * returned. if a shorter list exists it is resized, copied, and returned.
+ * @param size total number of entries to be allocated
+ * @param count number of populated entries
+ */
+etchparentinfo* get_vtab_inheritance_list(etch_object* obj, const short size, const short count, const short vtabclass)
+{
+ etchparentinfo *oldlist = NULL, *newlist = NULL;
+
+ /* if such a list is already cached, return it now */
+ vtabmask* vtab = ((etch_object*)obj)->vtab? obj->vtab: etch_cache_find(get_vtable_cachehkey(vtabclass), 0);
+ oldlist = vtab? vtab->inherits_from: NULL;
+
+ if (oldlist && oldlist->o.list_size >= size)
+ {
+ oldlist[0].c.list_count = count;
+ ((etch_object*)obj)->vtab = vtab;
+ return oldlist;
+ }
+
+ newlist = new_etch_inheritance_list(size, count, oldlist);
+ if(newlist == NULL) return NULL;
+
+ /* note that we are creating a placeholder vtable here. we could not add
+ * virtuals to such a vtable, since this vtable consists of the vtable
+ * header only, per sizeof(vtabmask), following.
+ */
+ if(vtab == NULL) {
+ vtab = new_vtable(NULL, sizeof(vtabmask), vtabclass);
+ vtab->inherits_from = newlist;
+ etch_cache_insert(((etch_object*)vtab)->get_hashkey(vtab), vtab, FALSE);
+ ((etch_object*)obj)->vtab = vtab;
+ }
+
+ vtab->inherits_from = newlist;
+ return newlist;
+}
+
+
+/**
+ * new_etch_inheritance_list()
+ * allocate and return an inheritance list of the specified size
+ * @param size total number of entries to be allocated
+ * @param count number of populated entries
+ */
+etchparentinfo* new_etch_inheritance_list(const short size, const short count,
+ etchparentinfo* oldlist)
+{
+ etchparentinfo *newlist = NULL;
+ const int newbytes = size * sizeof(etchparentinfo), MAXPARENTS = 15;
+ if (count < 0 || count > MAXPARENTS || size < 0 || count > size)
+ return NULL;
+
+ newlist = etch_malloc(newbytes, 0); /* vtables not tracked */
+ memset(newlist, 0, newbytes);
+
+ if (oldlist) /* we may be expanding an existing list, copy into new list */
+ { const int oldbytes = oldlist->o.list_size * sizeof(etchparentinfo);
+ memcpy(newlist, oldlist, oldbytes);
+// free(oldlist); /* vtables not tracked */
+// oldlist = NULL;
+ }
+ else newlist[0].c.list_count = count;
+
+ newlist[0].o.list_size = size; /* list attributes are in first entry */
+ return newlist;
+}
+
+
+/**
+ * is_derives_from_object()
+ * determine if specified object derives from object.
+ * all etch_object-masked objects are etch c objects by definition; however it is
+ * here that we would artificially specify that certain wrapped objects are not
+ * derived from object in the logical etch sense, if such a need arises.
+ */
+int is_derives_from_object(etch_object* obj)
+{
+ return obj? is_derives_from_object_class(((etch_object*)obj)->class_id): FALSE;
+}
+
+
+/**
+ * is_derives_from_object_class()
+ * see comments at is_derives_from_object()
+ */
+int is_derives_from_object_class(const unsigned short class_id)
+{
+ return TRUE;
+}
+
+
+/**
+ * get_next_etch_parent()
+ * see comments at get_next_etch_parentex() below
+ */
+etchparentinfo* get_next_etch_parent(etch_object* obj, int current_index)
+{
+ etchparentinfo* inherit_list = obj && ((etch_object*)obj)->vtab? obj->vtab->inherits_from: NULL;
+ return get_next_etch_parentex(((etch_object*)obj)->class_id, inherit_list, current_index);
+}
+
+
+/**
+ * get_next_etch_parentex()
+ * returns a non-disposable reference to etchparentinfo struct containing the
+ * class of next parent in this object's inheritance hierarchy, relative to the
+ * specified index. if specified object does in fact inherit from other than
+ * object, its inheritance list must have been previously instantiated via
+ * get_vtab_inheritance_list(), above, and populated accordingly, presumably
+ * int the object's constructor. the inheritance list implicitly ends with
+ * object, if the specified object's class derives from object; however object
+ * does not explicitly appear in the list and in fact must not be so populated.
+ * @param obj the etch object for which a parent is requested.
+ * @param current_index the index of the currently requested parent. on the
+ * first call specify zero, on subsequent calls increment current_index.
+ * @return etchparentinfo as described above, or NULL if no more parents.
+ * the returned reference is valid while its containing inheritance list remains
+ * instantiated, which ordinarily is while its associated vtable exists, which
+ * ordinarily is until service teardown.
+ */
+etchparentinfo* get_next_etch_parentex
+(const unsigned short class_id, etchparentinfo* inherit_list, int current_index)
+{
+ static etchparentinfo object_parent = { {ETCHTYPEB_ETCHOBJECT}, {CLASSID_OBJECT} };
+ etchparentinfo* nextparent = NULL;
+
+ if (NULL == inherit_list && current_index > 0);
+ else
+ if ((NULL == inherit_list) || (current_index == inherit_list[0].c.list_count))
+ if (is_derives_from_object_class(class_id))
+ nextparent = &object_parent;
+ else;
+ else
+ if (current_index < inherit_list[0].c.list_count)
+ nextparent = &inherit_list[++current_index]; /* list is one-based */
+
+ return nextparent;
+}
+
+
+/**
+ * new_primitive()
+ * allocate, initialize and return a primitive object
+ */
+etch_object* new_primitive(const unsigned obj_len, const unsigned short class_id)
+{
+ etch_object* newobj = new_object(obj_len, ETCHTYPEB_PRIMITIVE, class_id);
+ newobj->destroy = destroy_object;
+ newobj->clone = clone_object;
+ newobj->get_hashkey = etch_number_get_hashkey;
+ return newobj;
+}
+
+
+/**
+ * new_wchar()
+ * wide character string clone
+ */
+wchar_t* new_wchar(const wchar_t* s)
+{
+#ifdef WIN32
+ #pragma warning(disable:4996) /* wcscpy unsafe warning */
+#endif
+ unsigned bytelen;
+ wchar_t* clone;
+ if (NULL == s) return NULL;
+ bytelen = (unsigned)(wcslen(s) + 1) * sizeof(wchar_t);
+ clone = etch_malloc(bytelen, ETCHTYPEB_STRING);
+ wcscpy(clone, s);
+ return clone;
+}
+
+
+/**
+ * new_char()
+ * narrow character string clone
+ */
+char* new_char(const char* s)
+{
+ char* clone;
+ if (NULL == s) return NULL;
+ clone = etch_malloc(strlen(s) + 1, ETCHTYPEB_STRING);
+ return strcpy(clone, s);
+}
+
+
+/**
+ * new_byte()
+ */
+etch_byte* new_byte(const signed char v)
+{
+ etch_byte* newobj = (etch_byte*) new_primitive
+ (sizeof(struct etch_byte), CLASSID_PRIMITIVE_BYTE);
+ newobj->value = v;
+ //newobj->get_hashkey((etch_object*)newobj);
+ return newobj;
+}
+
+
+/**
+ * new_boolean()
+ */
+etch_boolean* new_boolean(boolean v)
+{
+ etch_boolean* newobj = (etch_boolean*) new_primitive
+ (sizeof(struct etch_boolean), CLASSID_PRIMITIVE_BOOL);
+ newobj->value = v? TRUE: FALSE;
+ //newobj->get_hashkey((etch_object*)newobj);
+ return newobj;
+}
+
+
+/**
+ * new_int8()
+ */
+etch_int8* new_int8(signed char v)
+{
+ etch_int8* newobj = (etch_int8*) new_primitive
+ (sizeof(struct etch_int8), CLASSID_PRIMITIVE_INT8);
+ newobj->value = v;
+ //newobj->get_hashkey((etch_object*)newobj);
+ return newobj;
+}
+
+
+/**
+ * new_int16()
+ */
+etch_int16* new_int16(short v)
+{
+ etch_int16* newobj = (etch_int16*) new_primitive
+ (sizeof(struct etch_int16), CLASSID_PRIMITIVE_INT16);
+ newobj->value = v;
+ //newobj->get_hashkey((etch_object*)newobj);
+ return newobj;
+}
+
+
+/**
+ * new_int32()
+ */
+etch_int32* new_int32(int v)
+{
+ etch_int32* newobj = (etch_int32*) new_primitive
+ (sizeof(struct etch_int32), CLASSID_PRIMITIVE_INT32);
+ newobj->value = v;
+ //newobj->get_hashkey((etch_object*)newobj);
+ return newobj;
+}
+
+
+/**
+ * new_int64()
+ */
+etch_int64* new_int64(int64 v)
+{
+ etch_int64* newobj = (etch_int64*) new_primitive
+ (sizeof(struct etch_int64), CLASSID_PRIMITIVE_INT64);
+ newobj->value = v;
+ //newobj->get_hashkey((etch_object*)newobj);
+ return newobj;
+}
+
+
+/**
+ * new_float()
+ */
+etch_float* new_float(float v)
+{
+ etch_float* newobj = (etch_float*) new_primitive
+ (sizeof(struct etch_float), CLASSID_PRIMITIVE_FLOAT);
+ newobj->value = v;
+ //newobj->get_hashkey((etch_object*)newobj);
+ return newobj;
+}
+
+
+/**
+ * new_double()
+ */
+etch_double* new_double(double v)
+{
+ etch_double* newobj = (etch_double*) new_primitive
+ (sizeof(struct etch_double), CLASSID_PRIMITIVE_DOUBLE);
+ newobj->value = v;
+ //newobj->get_hashkey((etch_object*)newobj);
+ return newobj;
+}
+
+
+int32 etch_number_as_int32(const void* object)
+{
+ if(is_etch_byte(object)){
+ return (int32)((etch_byte*)object)->value;
+ }else if(is_etch_int16(object)){
+ return (int32)((etch_int16*)object)->value;
+ }else if(is_etch_int32(object)){
+ return (int32)((etch_int32*)object)->value;
+ }
+ return 0;
+}
+
+/**
+ * etch_string_get_hashkey
+ * hashkey computation override for an etch_string.
+ * hash key is computed using the raw string as hash source.
+ */
+uint32 etch_string_get_hashkey(void* data)
+{
+ etch_object* etchobj = (etch_object*)data;
+
+ etch_string* sobj = (etch_string*) etchobj;
+ ((etch_object*)sobj)->hashkey = ETCH_ENCODING_ASCII != sobj->encoding?
+ etch_get_wchar_hashkey(sobj->v.valw):
+ etch_get_char_hashkey(sobj->v.valc);
+ return ((etch_object*)sobj)->hashkey;
+}
+
+static unsigned int utf8_string_length(const void* s) {
+ unsigned int res = 0;
+ const char* help = (const char*) s;
+ while (*help) {
+ if ((*help & 0xC0) != 0x80) {
+ ++res;
+ }
+ ++help;
+ }
+ return res;
+}
+
+static unsigned int ucs_string_length(const void* s, int width) {
+ unsigned int res = 0;
+
+ switch (width) {
+ case 1: {
+ const char* help = (const char*)s;
+ while (1) {
+ if (*help == 0) {
+ break;
+ }
+ ++help;
+ ++res;
+ }
+ break;
+ }
+ case 2: {
+ const int16* help = (const int16*)s;
+ while (1) {
+ if (*help == 0) {
+ break;
+ }
+ ++help;
+ ++res;
+ }
+ break;
+ }
+ case 4: {
+ const int32* help = (const int32*)s;
+ while (1) {
+ if (*help == 0) {
+ break;
+ }
+ ++help;
+ ++res;
+ }
+ break;
+ }
+ default:
+ ETCH_ASSERT(!"only widths of 2 and 4 supported");
+ break;
+ }
+
+ return res;
+}
+
+/**
+ * etch_string_init()
+ * private constructor for opaque string
+ */
+etch_string* etch_string_init(const void* s, const unsigned char encoding)
+{
+ etch_string* newobj = (etch_string*)new_primitive(sizeof(struct etch_string), CLASSID_STRING);
+
+ newobj->encoding = encoding;
+
+ if (s) {
+ switch(encoding) {
+ case ETCH_ENCODING_ASCII:
+ newobj->char_count = ucs_string_length(s, 1);
+ newobj->byte_count = (newobj->char_count + 1) * 1;
+ break;
+ case ETCH_ENCODING_UCS2:
+ newobj->char_count = ucs_string_length(s, 2);
+ newobj->byte_count = (newobj->char_count + 1) * 2;
+ break;
+ case ETCH_ENCODING_UCS4:
+ newobj->char_count = ucs_string_length(s, 4);
+ newobj->byte_count = (newobj->char_count + 1) * 4;
+ break;
+ case ETCH_ENCODING_UTF8:
+ newobj->char_count = utf8_string_length(s);
+ newobj->byte_count = (unsigned int)(strlen(s) + 1);
+ break;
+ default:
+ newobj->char_count = 0;
+ newobj->byte_count = 0;
+ printf("encoding: %d\n", encoding);
+ ETCH_ASSERT(!"encoding not supported");
+ break;
+ }
+ } else {
+ newobj->char_count = 0;
+ newobj->byte_count = 0;
+ ((etch_object*)newobj)->is_null = TRUE;
+ }
+
+ ((etch_object*)newobj)->destroy = destroy_string;
+ ((etch_object*)newobj)->clone = clone_string;
+
+ return newobj;
+}
+
+/**
+ * new_string()
+ * clones supplied string
+ * @param s a raw string to be assigned to the new string object.
+ * caller retains ownership of s.
+ */
+etch_string* new_string(const void* s, const unsigned char encoding)
+{
+ etch_string* newobj = etch_string_init(s, encoding);
+
+ if (s)
+ {
+ newobj->v.valc = etch_malloc(newobj->byte_count, ETCHTYPEB_STRING);
+ memcpy(newobj->v.valc, s, (size_t)newobj->byte_count);
+ }
+
+ ((etch_object*)newobj)->get_hashkey = etch_string_get_hashkey;
+ //newobj->get_hashkey((etch_object*)newobj);
+
+ return newobj;
+}
+
+
+/**
+ * new_stringw()
+ * convenience constructor for string type ETCH_ENCODING_UTF16;
+ * @param s a raw string to be assigned to the new string object.
+ * caller retains ownership of s.
+ */
+etch_string* new_stringw(const void* s)
+{
+ switch (sizeof(wchar_t)) {
+ case 2:
+ return new_string(s, ETCH_ENCODING_UCS2);
+ case 4:
+ return new_string(s, ETCH_ENCODING_UCS4);
+ default:
+ ETCH_ASSERT(!"illegal sizeof(wchar_t)");
+ return 0;
+ }
+}
+
+
+/**
+ * new_stringa()
+ * convenience constructor for string type ETCH_ENCODING_UTF8);
+ * @param s a raw string to be assigned to the new string object.
+ * caller retains ownership of s.
+ */
+etch_string* new_stringa(const void* s)
+{
+ return new_string(s, ETCH_ENCODING_UTF8);
+}
+
+
+/**
+ * new_string_from()
+ * does not clone supplied string
+ * @param s a disposable raw string to be assigned to the new string object.
+ * caller relinquishes ownership of s.
+ */
+etch_string* new_string_from(const void* s, const unsigned char encoding)
+{
+ etch_string* newobj = etch_string_init(s, encoding);
+ newobj->v.value = (void*) s;
+ ((etch_object*)newobj)->get_hashkey = etch_string_get_hashkey;
+ return newobj;
+}
+
+
+/**
+ * new_etch_event()
+ */
+etch_event* new_etch_event(const unsigned short class_id, const int value)
+{
+ etch_event* newobj = (etch_event*) new_int32(value);
+ if (class_id) ((etch_object*)newobj)->class_id = class_id;
+ return newobj;
+}
+
+
+/**
+ * new_etch_query()
+ */
+etch_query* new_etch_query(const unsigned short class_id, const int value)
+{
+ etch_query* newobj = (etch_query*) new_int32(value);
+ if (class_id) ((etch_object*)newobj)->class_id = class_id;
+ return newobj;
+}
+
+
+/**
+ * new_etch_control()
+ */
+etch_control* new_etch_control(const unsigned short class_id, const int value)
+{
+ etch_control* newobj = (etch_control*) new_int32(value);
+ if (class_id) ((etch_object*)newobj)->class_id = class_id;
+ return newobj;
+}
+
+
+/**
+ * new_date()
+ */
+etch_date* new_date()
+{
+ etch_date* newobj = (etch_date*)
+ new_primitive(sizeof(struct etch_date), CLASSID_DATE);
+
+ time (&newobj->value);
+ newobj->ticks = clock();
+
+ return newobj;
+}
+
+
+/**
+ * new_who()
+ * a who is an etch_object wrapper around some etch object type, its purpose
+ * to be a disposable object which opaquely specifies the object which is the
+ * sender or receiver component of a method.
+ * @param whoobj the object which is the actual source or destination.
+ * if this object is a nondisposable refrerence, of course it must be assured
+ * that the object is not destroyed prior to destruction of the etch_who
+ * which references it.
+ */
+etch_who* new_who(void* whoobj)
+{
+ etch_who* newobj = (etch_who*) new_object(sizeof(etch_who),ETCHTYPEB_ETCHOBJECT,CLASSID_WHO);
+ newobj->value = whoobj;
+ return newobj;
+}
+
+/**
+ * new_nullobj()
+ * instantiate and return a logically null object
+ */
+etch_object* new_nullobj()
+{
+ etch_object* obj = (etch_object*) new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT,CLASSID_NONE);
+ obj->is_null = TRUE;
+ return obj;
+}
+
+/**
+ * etch_get_char_hashkey
+ * hashkey computation using a narrow string as source
+ */
+unsigned etch_get_char_hashkey(const char* s)
+{
+ unsigned keybytelen = 0, hashkey = 0;
+ if (NULL != s)
+ keybytelen = (unsigned) strlen(s);
+ if (keybytelen)
+ hashkey = etchhash(s, keybytelen, 0);
+ return hashkey;
+}
+
+
+/**
+ * etch_get_wchar_hashkey
+ * hashkey computation using a unicode string as source
+ */
+unsigned etch_get_wchar_hashkey(const wchar_t* s)
+{
+ unsigned keybytelen = 0, hashkey = 0;
+ if (NULL != s)
+ keybytelen = (unsigned) (wcslen(s) * sizeof(wchar_t));
+ if (keybytelen)
+ hashkey = etchhash(s, keybytelen, 0);
+ return hashkey;
+}
+
+
+/**
+ * etch_number_get_hashkey
+ * hashkey computation override for an etch wrapped primitive number.
+ * hash key is computed using the numeric value as hash source.
+ */
+uint32 etch_number_get_hashkey(void* data)
+{
+ etch_object* etchobj = (etch_object*)data;
+ unsigned bytelength, hashkey;
+
+ switch(((etch_object*)etchobj)->class_id)
+ { case CLASSID_PRIMITIVE_INT32:
+ bytelength = 4;
+ hashkey = etchhash(&((etch_int32*)etchobj)->value, bytelength, 0);
+ break;
+ case CLASSID_PRIMITIVE_FLOAT:
+ bytelength = 4;
+ hashkey = etchhash(&((etch_float*)etchobj)->value, bytelength, 0);
+ break;
+ case CLASSID_PRIMITIVE_INT64:
+ bytelength = 8;
+ hashkey = etchhash(&((etch_int64*)etchobj)->value, bytelength, 0);
+ break;
+ case CLASSID_PRIMITIVE_DOUBLE:
+ bytelength = 8;
+ hashkey = etchhash(&((etch_double*)etchobj)->value, bytelength, 0);
+ break;
+ case CLASSID_DATE:
+ bytelength = 8;
+ hashkey = etchhash(&((etch_date*)etchobj)->value, bytelength, 0);
+ break;
+ case CLASSID_PRIMITIVE_INT16:
+ bytelength = 2;
+ hashkey = etchhash(&((etch_int16*)etchobj)->value, bytelength, 0);
+ break;
+ default:
+ bytelength = 1;
+ hashkey = etchhash(&((etch_int8*)etchobj)->value, bytelength, 0);
+ break;
+ }
+
+ return etchobj->hashkey = hashkey;
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_object_types.c b/binding-c/runtime/c/src/main/common/etch_object_types.c
new file mode 100644
index 0000000..76959dd
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_object_types.c
@@ -0,0 +1,40 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_object_types.h -- constants for internal object types.
+ */
+
+#include "etch.h"
+#include "etch_objecttypes.h"
+
+static const char* objtype_b_names[] = {
+ "ETCHTYPEB_UNDEFINED", // 0x00 also "ETCHTYPEB_NONE"
+ "ETCHTYPEB_BYTE", // 0x01
+ "ETCHTYPEB_BOOL", // 0x02
+ "ETCHTYPEB_INT8", // 0x03
+ "ETCHTYPEB_INT16", // 0x04
+ "ETCHTYPEB_INT32", // 0x05
+ "ETCHTYPEB_INT64", // 0x06
+ "ETCHTYPEB_IEEE32", // 0x07
+};
+
+const char* etch_object_type_get_name(objtype_b type)
+{
+ return objtype_b_names[type];
+}
\ No newline at end of file
diff --git a/binding-c/runtime/c/src/main/common/etch_runtime.c b/binding-c/runtime/c/src/main/common/etch_runtime.c
new file mode 100644
index 0000000..1628ba3
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_runtime.c
@@ -0,0 +1,281 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_runtime.h -- runtime methods.
+ */
+
+#include "etch_runtime.h"
+#include "etch_cache.h"
+#include "etch_default_value_factory.h"
+#include "etch_mem.h"
+#include "etch_mutex.h"
+#include "etchinternal_single_linked_list.h"
+#include "apr_pools.h"
+#include "apr_thread_mutex.h"
+#include "etch_encoding.h"
+
+#define MAX_SHUTDOWN_HOOKS_SIZE 5
+
+// global data
+apr_pool_t* g_etch_main_pool = NULL;
+apr_thread_mutex_t* g_etch_main_pool_mutex = NULL;
+
+static etch_runtime_shutdown_hook_func shutdown_hooks[5] =
+ {NULL,NULL,NULL,NULL,NULL};
+
+static uint16 etch_runtime_version_major = 1;
+static uint16 etch_runtime_version_minor = 1;
+static uint16 etch_runtime_version_revision = 2;
+
+//TODO
+etch_global_constants etchgc;
+
+/**
+ * etch_instantiate_global_constants()
+ * instantiate global constants. these are constants which can't be statically intialized.
+ * some of these may need to be freed in etch_free_global_constants in order that tests
+ * can show all memory freed. any etch objects instantiated here may need to have their
+ * is_static marker set in order that they remain instantiated throughout execution.
+ */
+void etch_instantiate_global_constants()
+{
+ etchgc.etch_charsetname_us_ascii = L"us-ascii";
+ etchgc.etch_charsetname_utf8 = L"utf-8";
+ etchgc.etch_charsetname_utf16 = L"utf-16";
+ etchgc.pctd = "%d";
+}
+
+/* runtime data */
+struct etch_runtime_t
+{
+ uint32 count; /* ref counting value */
+ unsigned char state; /* state or runtime*/
+ etch_config_t* config;
+ unsigned char config_owned;
+ etch_log_t* logger;
+};
+
+/* init runtime data */
+static struct etch_runtime_t etch_runtime = {
+ 0,
+ 0,
+ NULL,
+ 0,
+ NULL
+};
+
+static etch_status_t etch_statics_create()
+{
+ etch_instantiate_global_constants();
+
+ return ETCH_SUCCESS;
+}
+
+int etch_runtime_mem_abort(int retcode)
+{
+ printf("*** APR MEMORY ERROR! Code=%d***\n", retcode);
+ // sleep 1 s
+ apr_sleep(1000 * 1000);
+ return APR_SUCCESS;
+}
+
+etch_status_t etch_runtime_initialize(etch_config_t* config)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ apr_status_t apr_status = APR_SUCCESS;
+
+ if(etch_runtime.count++ > 0) {
+ return ETCH_SUCCESS;
+ }
+
+ if(etch_runtime.state == 0) {
+ // initialize apr
+ apr_status = apr_initialize();
+ if(apr_status != APR_SUCCESS) {
+ // log error
+ return -1;
+ }
+ // create apr memory pool
+
+ apr_status = apr_pool_create(&g_etch_main_pool, NULL);
+ //printf("1 creating apr pool %p\n",g_etch_main_pool);
+ if(apr_status != APR_SUCCESS) {
+ // log error
+ // shutdown apr
+ apr_terminate();
+ return ETCH_ERROR;
+ }
+ /* set pool abort function */
+ apr_pool_abort_set(etch_runtime_mem_abort, g_etch_main_pool);
+ /* set apr pool tag */
+ apr_pool_tag(g_etch_main_pool, "etch_apr_pool");
+
+ apr_status = apr_thread_mutex_create(&g_etch_main_pool_mutex, APR_THREAD_MUTEX_NESTED, g_etch_main_pool);
+ if(apr_status != APR_SUCCESS) {
+ // log error
+ // shutdown apr
+ apr_terminate();
+ return ETCH_ERROR;
+ }
+
+ etch_status = etch_encoding_initialize();
+ if(etch_status != ETCH_SUCCESS) {
+ apr_terminate();
+ return ETCH_ERROR;
+ }
+
+ // set configuration
+ if(config == NULL) {
+ etch_status = etch_config_create(&config);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+ etch_runtime.config_owned = 1;
+ }
+ etch_runtime.config = config;
+
+ // create logger
+ etch_status = etch_log_create(&etch_runtime.logger, etch_runtime.config);
+ if(etch_status != ETCH_SUCCESS) {
+ // error opening logger
+ }
+
+ // create etch cache
+ etch_status = etch_cache_create();
+ if(etch_status != ETCH_SUCCESS) {
+ // error opening logger
+ }
+
+ // create etch statics
+ etch_status = etch_statics_create();
+ if(etch_status != ETCH_SUCCESS) {
+ // error opening logger
+ }
+
+ // set initialized flag
+ etch_runtime.state = 1;
+ }
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_runtime_get_version(uint16* major, uint16* minor, uint16* revision)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+
+ if(major == NULL || minor == NULL || revision == NULL) {
+ return ETCH_EINVAL;
+ }
+ *major = etch_runtime_version_major;
+ *minor = etch_runtime_version_minor;
+ *revision = etch_runtime_version_revision;
+ return rv;
+}
+
+etch_status_t etch_runtime_get_config(etch_config_t** config)
+{
+ if(config == NULL) {
+ return ETCH_EINVAL;
+ }
+ *config = etch_runtime.config;
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_runtime_get_logger(etch_log_t** logger)
+{
+ if(logger == NULL) {
+ return ETCH_EINVAL;
+ }
+ *logger = etch_runtime.logger;
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_runtime_shutdown()
+{
+ int i = 0;
+ etch_status_t etch_status;
+
+ if(etch_runtime.count > 0) {
+ if(etch_runtime.count-- > 1) {
+ return ETCH_SUCCESS;
+ }
+ }
+
+ if(etch_runtime.state == 1) {
+
+ // destroy etch statics
+ for(; i < MAX_SHUTDOWN_HOOKS_SIZE; i++) {
+ if(shutdown_hooks[i]){
+ shutdown_hooks[i]();
+ }
+ }
+
+ etchvf_free_builtins();
+
+ // destroy cache
+ etch_cache_destroy();
+ // destroy logger
+ if(etch_runtime.logger != NULL) {
+ etch_log_destroy(etch_runtime.logger);
+ etch_runtime.logger = NULL;
+ }
+ // destroy config
+ if(etch_runtime.config_owned) {
+ etch_config_destroy(etch_runtime.config);
+ etch_runtime.config = NULL;
+ etch_runtime.config_owned = 0;
+ }
+
+ etch_status = etch_encoding_shutdown();
+ if(etch_status != ETCH_SUCCESS) {
+ apr_terminate();
+ return ETCH_ERROR;
+ }
+
+ /* destroy apr memory pool mutex */
+ apr_thread_mutex_destroy(g_etch_main_pool_mutex);
+
+
+ /* destroy apr memory pool */
+ //printf("1 destroying apr pool %p\n",g_etch_main_pool);
+ apr_pool_destroy(g_etch_main_pool);
+
+
+ g_etch_main_pool = NULL;
+
+ /* terminate apr */
+ apr_terminate();
+ /* set shutdown flag */
+ etch_runtime.state = 0;
+ }
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_runtime_shutdown_hook_add(etch_runtime_shutdown_hook_func func)
+{
+ int i = 0;
+ for(; i < MAX_SHUTDOWN_HOOKS_SIZE; i++)
+ {
+ if(shutdown_hooks[i] == NULL)
+ {
+ shutdown_hooks[i] = func;
+ return ETCH_SUCCESS;
+ }
+ }
+ return ETCH_ERROR;
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_thread.c b/binding-c/runtime/c/src/main/common/etch_thread.c
new file mode 100644
index 0000000..ec9fb26
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_thread.c
@@ -0,0 +1,932 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etchthread.c
+ * threads, thread pool
+ */
+#include "etch_thread.h"
+#include "etch_log.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+
+const char* LOG_CATEGORY = "etch_thread";
+
+extern apr_pool_t* g_etch_main_pool;
+extern apr_thread_mutex_t* g_etch_main_pool_mutex;
+
+// TODO: refactoring this stuff
+
+etch_threadpool* global_free_threadpool;
+
+size_t etch_get_threadid(void* threadstruct);
+int default_on_threadstart(void* param);
+int default_on_threadexit (void* param);
+int destroy_thread (void*);
+int etch_thread_start(void*);
+int etch_thread_stop(void*);
+int destroy_threadparams(etch_thread*);
+etch_mutex* loglock; // remove ??
+
+unsigned short etch_threadpool_id_farm;
+unsigned thread_id_farm;
+etch_mutex* loglock; // remove ??
+
+#ifdef APR_POOL_DEBUG
+APR_DECLARE(apr_size_t) apr_pool_num_bytes(apr_pool_t *p, int recurse);
+#endif
+
+typedef struct os_thread_and_etch_thread {
+ apr_os_thread_t* os_thread;
+ etch_thread* etch_thread;
+} os_thread_and_etch_thread;
+
+int etch_thread_find_by_os_thread(void* data, void* to_find) {
+ apr_os_thread_t* os_thread_to_find = (apr_os_thread_t*)to_find;
+ os_thread_and_etch_thread* both = (os_thread_and_etch_thread*)data;
+
+ return both->os_thread == os_thread_to_find;
+}
+
+/**
+ * etch_apr_threadproc()
+ * internal thread proc. this is a wrapper around the user thread proc.
+ * @param data expected to point at an etch_thread_params, which contains the
+ * thread start amd stop handlers, and the real threadproc and threadproc data.
+ */
+static void* APR_THREAD_FUNC etch_apr_threadproc(apr_thread_t *thread, void *data)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_thread_params* params = (etch_thread_params*) data;
+ etch_thread_callback on_exit;
+ etch_thread* threadobj;
+ if(!params) return (void*)(-1);
+ on_exit = params->on_exit;
+ threadobj = params->etchobj;
+
+ if (threadobj && threadobj->waiter) /* wait for signal to start */
+ {
+ status = etch_wait_wait(threadobj->waiter, 1);
+ if(status != ETCH_SUCCESS) {
+ // erro
+ }
+
+ /* fyi we can't destroy the waiter here since etchwait_signal
+ * holds etchwait.mutex and will try to release it.
+ * destroy_etchwait (threadobj->waiter); no!
+ * threadobj->waiter = NULL;
+ */
+ }
+
+ params->threadstate = ETCH_THREADSTATE_STARTED;
+
+ if (params->on_start) /* call threadproc start hook */
+ params->on_start(threadobj);
+
+ if (params->threadproc) /* run user threadproc */
+ params->threadproc(params);
+
+ params->threadstate = ETCH_THREADSTATE_STOPPED;
+
+
+ if (on_exit) /* call threadproc exit hook */
+ on_exit(threadobj);
+
+ apr_thread_exit(thread, APR_SUCCESS);
+
+ return 0;
+}
+
+
+/**
+ * new_thread()
+ * etch_thread constructor
+ */
+etch_thread* new_thread (etch_threadproc proc, void* tdata)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_thread_params* tp = NULL;
+ apr_pool_t* newsubpool = NULL;
+ etch_thread* newthread = NULL;
+ /* size_t poolsize = apr_pool_num_bytes(get_etch_aprpool(), FALSE); */
+
+ newthread = (etch_thread*) new_object
+ (sizeof(etch_thread), ETCHTYPEB_THREAD, CLASSID_THREAD);
+
+ ((etch_object*)newthread)->destroy = destroy_thread;
+ ((etch_object*)newthread)->clone = clone_null;
+
+ tp = &newthread->params;
+ etch_init_threadparams(tp);
+ tp->etchobj = newthread;
+ tp->libdata = &newthread->aprdata;
+ tp->on_start = default_on_threadstart;
+ tp->on_exit = default_on_threadexit;
+ tp->etch_thread_id = next_etch_threadid();
+ tp->threadproc = proc;
+ tp->data = tdata;
+
+ newthread->start = etch_thread_start;
+ newthread->stop = etch_thread_stop;
+
+ // TODO: pool
+ status = etch_wait_create(&newthread->waiter, NULL);
+ if(status != ETCH_SUCCESS) {
+ // error log it
+ }
+ newthread->startCond = 0;
+
+ //newthread->waiter->cond_var = &newthread->startCond;
+
+ if (0 != etch_createthread(tp))
+ {
+ etch_wait_destroy(newthread->waiter);
+ etch_free(newthread);
+ newthread = NULL;
+ //printf("2 destroying apr pool %p\n",newsubpool);
+ apr_pool_destroy(newsubpool);
+
+ }
+
+ return newthread;
+}
+
+
+/**
+ * etch_createthread()
+ * to invoke: 1) initalize an etch_apr_threaddata with the mempool;
+ * 2) initialize an etch_thread_params with threadproc plus the etch_apr_threaddata,
+ * plus the thread user data.
+ * 2) call with the threaddata, and the userdata.
+ */
+int etch_createthread(etch_thread_params* params)
+{
+ int result = 0;
+ apr_status_t aprstatus;
+
+ if (!params->on_start) params->on_start = default_on_threadstart;
+ if (!params->on_exit) params->on_exit = default_on_threadexit;
+
+ /* fyi: this sets thread.aprdata.threadattr */
+ // TODO: pool
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ result = (APR_SUCCESS == apr_threadattr_create(¶ms->libdata->threadattr, g_etch_main_pool))? 0: -1;
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+ if (result == 0) {
+ // TODO: pool
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ aprstatus = apr_thread_create(¶ms->libdata->thread, params->libdata->threadattr, etch_apr_threadproc, params, g_etch_main_pool);
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+ if (aprstatus != APR_SUCCESS) {
+ char buffer[512];
+ apr_strerror(aprstatus, buffer, 512);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "thread create error error-code: %d error-msg: %s\n", aprstatus, buffer);
+ }
+ }
+ return result;
+}
+
+int find_etch_thread_by_os_thread(void* listentry, void* apr_os_thread)
+{
+ os_thread_and_etch_thread* pair = (os_thread_and_etch_thread*) listentry;
+ return pair->os_thread == apr_os_thread;
+}
+
+/**
+ * destroy_thread()
+ * etch_thread destructor
+ */
+int destroy_thread(void* data)
+{
+ etch_thread* threadx = (etch_thread*)data;
+
+
+ if (!is_etchobj_static_content(threadx))
+ {
+ if (threadx->waiter) {
+ etch_wait_destroy(threadx->waiter);
+ }
+ }
+
+ return destroy_objectex((etch_object*)threadx);
+}
+
+
+/**
+ * destroy_threadparams()
+ * free memory for any heap parameters passed to an etch_thread.
+ */
+int destroy_threadparams(etch_thread* thisx)
+{
+ etch_thread_params* params = &thisx->params;
+ void* userdata = params->data;
+
+ if (params->waitobj)
+ etch_wait_destroy(params->waitobj);
+
+ if (params->is_own_data)
+ if (params->is_data_etchobject)
+ ((etch_object*)userdata)->destroy(userdata);
+ else etch_free(userdata);
+
+ return 0;
+}
+
+
+/**
+ * etch_init_threadparams()
+ */
+void etch_init_threadparams(etch_thread_params* p)
+{
+ if (NULL == p) return;
+ memset(p, 0, sizeof(etch_thread_params));
+ p->signature = ETCH_THREAD_PARAMS_SIGNATURE;
+}
+
+
+/**
+ * etch_thread_start()
+ * default thread start method for etch_threads which wait for a start signal
+ */
+int etch_thread_start(void* data)
+{
+ etch_thread* threadx = (etch_thread*)data;
+ etch_status_t status = ETCH_SUCCESS;
+ int result = -1;
+ const int thread_id = threadx->params.etch_thread_id;
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "thread %d starting ...\n", thread_id);
+
+ if (threadx->waiter) {
+ status = etch_wait_set(threadx->waiter, 1);
+ // log error
+ }
+
+ /* etch_tcpsvr_acceptproc: while(lxr->is_started) was not seeing the started
+ * flag set, because the thread had not received control after the above signal.
+ * so this sleep forces it to run, the visual indication of the accept thread
+ * running being the "accepting ..." log message. TODO lose the sleep().
+ * NOTE that by the time this thread regains control, its connection may
+ * have been closed, or the started thread may possibly even have exited.
+ */
+ etch_sleep(30); /* see comments above */
+
+ /* there is currently no pressing need to log this info,
+ * and since it may indeed be stale, we'll not present it for now. */
+ #if(0)
+ threadstate = is_etch_thread(threadx)?
+ threadx->params.threadstate: ETCH_THREADSTATE_DEFUNCT;
+
+ switch(threadstate)
+ { case ETCH_THREADSTATE_STARTED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "thread %d started\n", thread_id);
+ break;
+ case ETCH_THREADSTATE_STOPPED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "thread %d stopped\n", thread_id);
+ break;
+ case ETCH_THREADSTATE_DEFUNCT:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "thread %d was started now destroyed\n", thread_id);
+ break;
+ default: ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "thread %d state %d\n", thread_id, threadstate);
+ }
+ #endif
+
+ return result;
+}
+
+
+/**
+ * etch_thread_stop()
+ * default etch_thread thread stop method.
+ * currently never invoked. causes serious grief.
+ */
+int etch_thread_stop(void* data)
+{
+ etch_thread* thread = (etch_thread*)data;
+ apr_status_t tresult = 0;
+ etch_thread_params* p = thread? &thread->params: NULL;
+ if (NULL == p) return -1;
+
+ tresult = apr_thread_detach(p->libdata->thread);
+
+ tresult = apr_thread_exit(p->libdata->thread, 0);
+
+ return APR_SUCCESS == tresult? 0: -1;
+}
+
+int etch_thread_current_id()
+{
+#if defined(WIN32)
+ return GetCurrentThreadId();
+#elif defined(__QNX__) || defined(__LINUX__) || defined(__APPLE__)
+ #warning not implemented for this os
+ return 0;
+#else
+ #error OS no support
+#endif
+
+ //int tid = -1;
+ //void* p = NULL;
+ //apr_os_thread_t thread_t = apr_os_thread_current();
+ //apr_os_threadkey_t threadkey_t;
+ //apr_os_threadkey_get(&threadkey_t, );
+ //p = thread_t;
+ ////tid = p;
+ //printf("%p", p);
+ //tid = *(int*)p;
+
+ return 0;
+}
+
+/**
+ * etch_threadpool_run_freethread()
+ * instantiate and possibly run a free thread.
+ */
+etch_thread* etch_threadpool_run_freethread (etch_threadpool* pool,
+ etch_threadproc threadproc, void* threaddata)
+{
+ etch_thread* newthread = NULL;
+ if (!pool || pool->is_defunct) return NULL;
+
+ /* create thread in a wait state. it may be started below. */
+ if (NULL == (newthread = new_thread (threadproc, threaddata)))
+ return NULL;
+
+ newthread->params.etchpool = pool;
+ newthread->params.is_own_data = pool->is_free_data;
+ newthread->params.is_data_etchobject = pool->is_data_etchobject;
+
+ etch_arraylist_add(pool->threadlist, newthread);
+ /* an etch_thread object in the threadpool gets freed when a thread exits,
+ * finds its threadpool, and removes itself from that pool's threadlist.
+ */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "thread %d created on pool %d\n",
+ newthread->params.etch_thread_id, pool->threadpool_id);
+
+ if (!pool->is_manual_start) /* start thread unless requested otherwise */
+ newthread->start(newthread);
+
+ return newthread;
+}
+
+
+/**
+ * etch_threadpool_run_poolthread()
+ * run a thread from a queued pool.
+ */
+etch_thread* etch_threadpool_run_poolthread (etch_threadpool* pool,
+ etch_threadproc threadproc, void* threaddata)
+{
+ if (!pool || pool->is_defunct) return NULL;
+ return NULL; /* we have not yet implemented or integrated a queued thread pool */
+}
+
+
+/**
+ * thread_cancel()
+ * cancel a running thread
+ */
+int etch_thread_cancel(etch_thread* thread)
+{
+ thread->params.threadstate = ETCH_THREADSTATE_STOPPING;
+ etch_join(thread);
+ return 0;
+}
+
+
+/**
+ * etch_join()
+ * block until specified thread exits
+ */
+int etch_join(etch_thread* thread)
+{
+ int result = 0;
+ etch_thread_params* tp = &thread->params;
+ apr_status_t tresult = 0; /* result returned from dead thread */
+ thread->is_joined = TRUE; /* mark to avoid on_exit destruction */
+ result = apr_thread_join (&tresult, tp->libdata->thread);
+ return result;
+}
+
+
+/**
+ * etch_thread_join()
+ * block until specified thread exits
+ */
+int etch_thread_join(etch_thread_params* params)
+{
+ apr_status_t tresult = 0; /* result returned from dead thread */
+ const int result = apr_thread_join(&tresult, params->libdata->thread);
+ return result;
+}
+
+
+/**
+ * etch_thread_yield()
+ */
+void etch_thread_yield()
+{
+ apr_thread_yield();
+}
+
+
+/**
+ * etch_thread_arraylist_comparator()
+ * arraylist comparator function to compare a specified thread ID
+ * with the ID of a specified etch_thread
+ */
+int etch_thread_arraylist_comparator (void* id, void* etchobj)
+{
+ const int this_id = (int) (size_t) id;
+ const int that_id = ((etch_thread*) etchobj)->params.etch_thread_id;
+ return this_id < that_id? -1: this_id > that_id? 1: 0;
+}
+
+
+/**
+ * threadpool_removeentry()
+ * remove a thread from a thread pool, not destroying the etch_thread content.
+ * @return the removed etch_thread object.
+ */
+etch_thread* threadpool_remove_entry (etch_threadpool* pool, const int etch_thread_id)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = -1, i = 0;
+ etch_thread* outthread = NULL;
+ ETCH_ASSERT(is_etch_threadpool(pool));
+ if (pool->is_defunct) return NULL;
+
+ status = etch_mutex_lock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+
+ i = etch_arraylist_indexof (pool->threadlist, (void*) (size_t) etch_thread_id,
+ 0, etch_thread_arraylist_comparator);
+
+ if (i >= 0)
+ { outthread = etch_arraylist_get (pool->threadlist, i);
+ result = etch_arraylist_remove (pool->threadlist, i, FALSE);
+ }
+
+ status = etch_mutex_unlock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+
+ return 0 == result? outthread: NULL;
+}
+
+
+/**
+ * threadpool_remove()
+ * remove a thread from a thread pool, destroying the etch_thread object.
+ * this should not be invoked by thread pool destructor, since is_defunct is then true.
+ */
+int threadpool_remove(etch_threadpool* pool, const int etch_thread_id)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = 0;
+ ETCH_ASSERT(is_etch_threadpool(pool));
+ if (pool->is_defunct) return -1;
+
+ status = etch_mutex_lock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+
+ result = etch_arraylist_remove_content (pool->threadlist,
+ (void*) (size_t) etch_thread_id, 0, etch_thread_arraylist_comparator);
+
+ status = etch_mutex_unlock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+
+ return result;
+}
+
+
+/**
+ * threadpool_waitfor_all()
+ * block until all threads in the pool exit.
+ * @param is_cancel if true, signal the thread to exit asap,
+ * otherwise wait for normal thread exit.
+ */
+int threadpool_waitfor_all(etch_threadpool* pool, const int is_cancel)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_iterator iterator;
+ etch_arraylist* listcopy = NULL;
+ int threadcount = 0, is_iterable = 0;
+ if (NULL == pool) return -1;
+
+ status = etch_mutex_lock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ if ((threadcount = pool->count(pool)) > 0 && !pool->is_iterating)
+ pool->is_iterating = is_iterable = TRUE;
+ status = etch_mutex_unlock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+
+ if (threadcount == 0) return 0; /* no active threads to wait for */
+ if (!is_iterable) return -1; /* another thread is iterating this pool */
+
+ /* iterate a copy since canceled threads remove themselves from their pool */
+ /* note that iterating the actual pool may however be preferable if the
+ * locking is done right, since if a thread ahead of the index exits
+ * during this iteration, it would presumably then not show up here. */
+ status = etch_mutex_lock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ listcopy = new_etch_arraylist_from(pool->threadlist);
+ status = etch_mutex_unlock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ listcopy->is_readonly = TRUE; /* so destructor will not free copied content */
+ set_iterator(&iterator, listcopy, &listcopy->iterable);
+
+ /* note that if pool.is_defunct is true, exiting threads which belong to
+ * that pool will not remove themselves from the pool and self-destruct.
+ * so, if is_defunct is true, threads exiting while we are iterating here
+ * will not be destroyed. the threadpool destructor destroy_threadpool()
+ * sets is_defunct. however if threadpool_waitforall() is invoked elsewhere,
+ * care must be taken to ensure that pool threads do not exit while we
+ * are iterating them here. */
+
+ while(iterator.has_next(&iterator)) /* wait for each pool thread to exit */
+ {
+ etch_thread* thisthread = (etch_thread*) iterator.current_value;
+ etch_thread* removedthread = NULL;
+
+ if (thisthread)
+ { const int thread_id = thisthread->params.etch_thread_id;
+ char x[60];
+ apr_snprintf(x, sizeof(x), "thread %d in pool %d", thread_id, pool->threadpool_id);
+
+ if (is_cancel)
+ { ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "canceling %s ...\n", x);
+ etch_thread_cancel (thisthread); /* BLOCK here */
+ }
+ else
+ { ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "joining %s ...\n", x);
+ etch_join (thisthread); /* BLOCK here */
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s ended\n", x);
+
+ status = etch_mutex_lock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ if (pool->pooltype == ETCH_THREADPOOLTYPE_FREE)
+ { removedthread = threadpool_remove_entry (pool, thread_id);
+ etch_object_destroy (thisthread);
+ }
+ status = etch_mutex_unlock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ }
+
+ iterator.next(&iterator);
+ }
+
+ pool->is_iterating = FALSE;
+ etch_object_destroy(listcopy); /* readonly set above so no content destroyed */
+ return 0;
+}
+
+
+/**
+ * destroy_threadpool()
+ * etch_threadpool destructor.
+ * @todo add logic for queued pool, or create separate destructor.
+ */
+int destroy_threadpool(void* data)
+{
+ etch_threadpool* pool = (etch_threadpool*)data;
+ etch_status_t status = ETCH_SUCCESS;
+ int can_destroy = TRUE;
+ if (NULL == pool) return 0;
+
+ status = etch_mutex_lock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ if (pool->is_defunct) /* ensure no race */
+ can_destroy = FALSE;
+ else pool->is_defunct = TRUE;
+ status = etch_mutex_unlock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ if (!can_destroy) return -1;
+
+ threadpool_waitfor_all (pool, TRUE); /* BLOCK until all threads exited */
+
+ /* destroy the threadlist, destroying the etch_thread content in the process.
+ * note that each thread's parameter list was destroyed as the thread exited.
+ * threadlist owns the mutex assigned to it and will destroy threadlist_lock.
+ */
+ if (!is_etchobj_static_content(pool))
+ {
+ etch_object_destroy(pool->threadlist);
+ status = etch_mutex_destroy(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ }
+
+ return destroy_objectex((etch_object*)pool);
+}
+
+
+/**
+ * threadpool_count()
+ * return count of threads in list
+ */
+int threadpool_count(etch_threadpool* pool)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int count = 0;
+ ETCH_ASSERT(pool);
+ status = etch_mutex_lock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ count = pool->threadlist? pool->threadlist->count: 0;
+ status = etch_mutex_unlock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ return count;
+}
+
+
+/**
+ * threadpool_thread()
+ * return thread[i] from specified threadpool.
+ * @param i the index.
+ * @return the etch_thread*, or null if no thread at that index.
+ */
+etch_thread* threadpool_thread(etch_threadpool* pool, const int i)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_thread* thisthread = NULL;
+ if (NULL == pool) return NULL;
+
+ status = etch_mutex_lock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ thisthread = i < pool->count(pool)? pool->threadlist->base[i]: NULL;
+ status = etch_mutex_unlock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ return thisthread;
+}
+
+
+/**
+ * new_threadpool_list()
+ * return an arraylist configured appropriately for a thread pool.
+ */
+etch_arraylist* new_threadpool_list(const int initsize, etch_mutex* mutex)
+{
+ //TODO: Check if arraylist has to be synchronized
+ etch_arraylist* list = new_etch_arraylist(initsize,0);
+ /* ETCHARRAYLIST_CONTENT_OBJECT lets list call destroy() on its content */
+ list->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
+ list->is_readonly = TRUE; /* list does not own content */
+ ((etch_object*)list)->synclock = mutex; /* list owns this mutex */
+ return list;
+}
+
+
+/**
+ * new_threadpool()
+ * etch_threadpool constructor
+ * @param pooltype ETCH_THREADPOOLTYPE_FREE (default); ETCH_THREADPOOLTYPE_QUEUED
+ * @param initsize initial number of threads, ignored for ETCH_THREADPOOLTYPE_FREE.
+ */
+etch_threadpool* new_threadpool(const unsigned pooltype, const int initsize)
+{
+ etch_status_t status = ETCH_SUCCESS;
+
+ etch_threadpool* pool = (etch_threadpool*) new_object
+ (sizeof(etch_threadpool), ETCHTYPEB_THREADPOOL, CLASSID_THREADPOOL);
+
+ ((etch_object*)pool)->destroy = destroy_threadpool;
+ ((etch_object*)pool)->clone = clone_null;
+ pool->count = threadpool_count;
+ pool->run = pooltype == ETCH_THREADPOOLTYPE_QUEUED?
+ etch_threadpool_run_poolthread : etch_threadpool_run_freethread;
+
+ // TODO: pool
+ status = etch_mutex_create(&pool->pool_lock, ETCH_MUTEX_NESTED, NULL);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+
+ // TODO: pool
+ status = etch_mutex_create(&pool->threadlist_lock, ETCH_MUTEX_NESTED, NULL);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+
+ pool->threadlist = new_threadpool_list(initsize, pool->threadlist_lock);
+
+ pool->is_free_data = TRUE;
+ pool->is_data_etchobject = FALSE;
+ pool->threadpool_id = ++etch_threadpool_id_farm;
+
+ return pool;
+}
+
+
+/**
+ * etch_glopool_exit()
+ * wait on all threads in global pool to exit, destroy global pool.
+ */
+int etch_glopool_exit()
+{
+ destroy_threadpool(global_free_threadpool);
+ global_free_threadpool = NULL;
+ return 0;
+}
+
+
+/**
+ * global_pool()
+ * return singleton global free thread pool
+ */
+etch_threadpool* etch_glopool()
+{
+ if (global_free_threadpool == NULL)
+ global_free_threadpool = new_threadpool(ETCH_THREADPOOLTYPE_FREE, 0);
+ return global_free_threadpool;
+}
+
+
+
+/**
+ * global_pool()
+ * return singleton global free thread pool
+ * todo destroy this pool somewhere
+ */
+etch_threadpool* global_pool()
+{
+ if (global_free_threadpool == NULL)
+ global_free_threadpool = new_threadpool(ETCH_THREADPOOLTYPE_FREE, 0);
+ return global_free_threadpool;
+}
+
+
+/**
+ * getter for APR thread id
+ * seems to be wrong - the ID is the same for each thread
+ */
+size_t etch_get_threadid (void* threadstruct)
+{
+ /* can't address apr_thread_t content so we remap part of apr_thread_t */
+ struct x { void* mempool; void* threadid; };
+ return threadstruct? (size_t) ((struct x*)threadstruct)->threadid: 0;
+}
+
+
+/**
+ * next_etch_threadid()
+ * get a sequential ID we use to key threads
+ */
+unsigned int next_etch_threadid()
+{
+ do
+ {
+ apr_atomic_inc32((volatile apr_uint32_t*) &thread_id_farm);
+ } while(thread_id_farm == 0);
+
+ return thread_id_farm;
+}
+
+
+/**
+ * default_on_threadstart()
+ * default thread start callback.
+ * the argument passed to start and exit callbacks is the etch_thread*
+ */
+int default_on_threadstart(void* param)
+{
+ int thread_id = 0;
+ etch_thread* threadx = (etch_thread*) param;
+ etch_thread_params* p = threadx? &threadx->params: 0;
+ if (p) thread_id = p->etch_thread_id;
+ ETCH_ASSERT(thread_id);
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "thread %d enter\n", thread_id);
+ return 0;
+}
+
+
+/**
+ * default_on_threadexit()
+ * default thread exit callback.
+ * the argument passed to start and exit callbacks is the etch_thread*
+ */
+int default_on_threadexit(void* param)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_thread* thisthread = (etch_thread*) param;
+ etch_thread* removedthread = NULL;
+ etch_threadpool* pool = NULL;
+ etch_thread_params* p;
+ int thread_id = 0;
+ ETCH_ASSERT(thisthread);
+ ETCH_ASSERT(is_etch_thread(thisthread));
+ p = &thisthread->params;
+ thread_id = p->etch_thread_id;
+ pool = p->etchpool;
+
+ /* we remove the thread from its threadpool if a free pool, and unless the
+ * pool is being destroyed, and unless the pool is otherwise being iterated
+ * elswhere - we can't destroy a thread that another thread has joined, for
+ * instance, the joiner will do that once unblocked. and we do not remove a
+ * pool entry if we are currently iterating the pool.
+ */
+ if (pool && !pool->is_defunct)
+ {
+ const int is_freethread = (pool->pooltype == ETCH_THREADPOOLTYPE_FREE);
+ char x[40]; sprintf(x, "thread %d from pool %d", thread_id, pool->threadpool_id);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "removing %s ...\n", x);
+
+ status = etch_mutex_lock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ /* todo dispose of thread elsewhere when is_iterating is true */
+
+ removedthread = threadpool_remove_entry (pool, thread_id);
+
+ if (NULL == removedthread) /* occasionally observed while in debugger */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_WARN, "%s was previously removed\n", x);
+ else
+ if (removedthread != thisthread) /* condition never observed to date */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s removed unexpectedly\n", x);
+ else {
+ /* if either the pool is configured to free threads on exit,
+ * or this is a free pool that we are not currently iterating over ... */
+ if (!removedthread->is_joined && is_freethread
+ && (pool->is_free_threads || !pool->is_iterating))
+ {
+ etch_object_destroy(removedthread);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "%s removed and destroyed\n", x);
+ }
+ else
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "%s removed\n", x);
+ }
+
+ status = etch_mutex_unlock(pool->pool_lock);
+ if(status != ETCH_SUCCESS) {
+ // error
+ }
+ }
+ else
+ destroy_threadparams(thisthread);
+
+ /* we always destroy the thread's caller-allocated parameter memory, but we
+ * do not however destroy the etch_thread wrapper when the thread is not in
+ * a threadpool. memory for non-pool threads is managed by caller.
+ */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "thread %d exit\n", thread_id);
+ return 0;
+}
+
+
+/**
+ * etch_sleep()
+ * interface to OS sleep in milliseconds
+ */
+void etch_sleep(const int ms)
+{
+ apr_sleep(ms * 1000);
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_thread2.c b/binding-c/runtime/c/src/main/common/etch_thread2.c
new file mode 100644
index 0000000..320c536
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_thread2.c
@@ -0,0 +1,125 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifdef _THREAD2_
+/*
+ * etcht_hread.c
+ */
+#include "etch_thread2.h"
+#include "etch_log.h"
+#include "etch_object.h"
+#include "etch_objecttypes.h"
+#include "etch_wait.h"
+#include "etch_mem.h"
+
+static const char* LOG_CATEGORY = "etch_thread";
+
+extern etch_pool_t* g_etch_main_pool;
+
+
+
+etch_status_t etch_thread_attr_create(etch_thread_attr_t** attr)
+{
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_thread_attr_set_detachstate(etch_thread_attr_t* attr, int state)
+{
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_thread_attr_get_detachstate(etch_thread_attr_t* attr, int* state)
+{
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_thread_attr_destroy(etch_thread_attr_t* attr)
+{
+ return ETCH_SUCCESS;
+}
+
+
+struct etch_thread_t
+{
+ etch_object object;
+
+ // thread member types
+ apr_thread_t* apr_thread;
+ apr_pool_t* apr_pool;
+
+ etch_thread_func func;
+ void* data;
+ etch_wait_t* event_start;
+};
+
+/**
+ * etch_apr_threadproc()
+ * internal thread proc. this is a wrapper around the user thread proc.
+ * @param data expected to point at an etch_thread_params, which contains the
+ * thread start amd stop handlers, and the real threadproc and threadproc data.
+ */
+static void* APR_THREAD_FUNC apr_thread_func(apr_thread_t* apr_thread, void *data)
+{
+ //etch_status_t rv = ETCH_SUCCESS;
+ //etch_status_t status = ETCH_SUCCESS;
+ //etch_thread_t* thread = NULL;
+
+ //thread = data;
+ //ETCH_ASSERT(thread == NULL);
+
+ //status = etch_wait_wait_and_set(thread->event_start, 1, 0);
+ //ETCH_ASSERT(status == ETCH_SUCCESS);
+
+ //etch_thread_exit(thread, rv);
+ return NULL;
+}
+
+
+etch_status_t etch_thread_create(etch_thread_t** thread, const etch_thread_attr_t* attr, etch_thread_func thread_proc, void* data)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ //etch_status_t status = ETCH_SUCCESS;
+ //etch_thread_t* newthread = NULL;
+ //apr_status_t apr_status = NULL;
+
+ //if(thread == NULL) {
+ // return ETCH_EINVAL;
+ //}
+
+ //newthread = (etch_thread_t*)new_object(sizeof(etch_thread_t), ETCHTYPEB_THREAD, CLASSID_THREAD);
+ //ETCH_ASSERT(newthread == NULL);
+
+
+ return rv;
+}
+
+etch_status_t etch_thread_join(etch_thread_t* thread, etch_status_t* status)
+{
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_thread_yield()
+{
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_thread_exit(etch_thread_t* thread, etch_status_t status)
+{
+ return ETCH_SUCCESS;
+}
+
+#endif
\ No newline at end of file
diff --git a/binding-c/runtime/c/src/main/common/etch_threadpool.c b/binding-c/runtime/c/src/main/common/etch_threadpool.c
new file mode 100644
index 0000000..90707a4
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_threadpool.c
@@ -0,0 +1,84 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_threadpool.c.c
+ */
+
+#include "etch_threadpool.h"
+#include "etch_object.h"
+#include "etch_objecttypes.h"
+#include "etch_mutex.h"
+
+/*
+static const char* LOG_CATEGORY = "etch_threadpool";
+*/
+static apr_uint32_t g_etch_threadpool_id = 0;
+
+struct etch_threadpool_t
+{
+ etch_object object;
+
+ // threadpool id
+ uint32 id;
+};
+
+etch_status_t etch_threadpool_create(etch_threadpool_t** threadpool, uint8 type, const uint16 init_size)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_threadpool_t* newthreadpool = NULL;
+
+ if(threadpool == NULL || (type != ETCH_THREADPOOL_TYPE_FREE && type != ETCH_THREADPOOL_TYPE_QUEUED)) {
+ return ETCH_EINVAL;
+ }
+
+ newthreadpool = (etch_threadpool_t*)new_object(sizeof(etch_threadpool_t), ETCHTYPEB_THREADPOOL, CLASSID_THREADPOOL);
+ ETCH_ASSERT(newthreadpool != NULL);
+
+ newthreadpool->id = apr_atomic_inc32(&g_etch_threadpool_id);
+
+ switch(type) {
+ case ETCH_THREADPOOL_TYPE_FREE:
+ break;
+ case ETCH_THREADPOOL_TYPE_QUEUED:
+ break;
+ default:
+ break;
+ }
+
+
+ *threadpool = newthreadpool;
+ return rv;
+
+}
+
+etch_status_t etch_threadpool_remove(etch_threadpool_t* threadpool, const uint16 thread_id)
+{
+ return ETCH_ENOTIMPL;
+}
+
+etch_status_t etch_threadpool_join(etch_threadpool_t* threadpool)
+{
+ return ETCH_ENOTIMPL;
+}
+
+etch_status_t etch_threadpool_destroy(etch_threadpool_t* threadpool)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ return rv;
+}
diff --git a/binding-c/runtime/c/src/main/common/etch_wait.c b/binding-c/runtime/c/src/main/common/etch_wait.c
new file mode 100644
index 0000000..7bc1468
--- /dev/null
+++ b/binding-c/runtime/c/src/main/common/etch_wait.c
@@ -0,0 +1,320 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_wait.c -- thread wait implementation
+ */
+
+#include "etch_wait.h"
+#include "etch_object.h"
+#include "etch_objecttypes.h"
+#include "etch_mutex.h"
+#include "etch_log.h"
+
+static const char* LOG_CATEGORY = "etch_wait";
+
+extern apr_pool_t* g_etch_main_pool;
+extern apr_thread_mutex_t* g_etch_main_pool_mutex;
+
+struct etch_wait_t
+{
+ etch_object object;
+
+ void* mutex; /* associated mutex - owned */
+ void* cond; /* wait implementation - owned */
+ int64 cond_var; /* wait condition var */
+ int32 threadcount; /* count of threads waiting */
+
+};
+
+etch_status_t etch_wait_create(etch_wait_t** waiter, etch_pool_t* pool)
+{
+ etch_wait_t* newwaiter = NULL;
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_cond_t* apr_cond = NULL;
+ apr_thread_mutex_t* apr_mutex = NULL;
+
+ if(waiter == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ /* if pool is null use global pool */
+ if(pool == NULL) {
+ pool = g_etch_main_pool;
+ }
+
+ newwaiter = (etch_wait_t*)new_object(sizeof(etch_wait_t), ETCHTYPEB_WAIT, CLASSID_WAIT);
+ ETCH_ASSERT(newwaiter != NULL);
+
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ apr_status = apr_thread_mutex_create(&apr_mutex, APR_THREAD_MUTEX_UNNESTED, g_etch_main_pool);
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+ if(apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ etch_free(newwaiter);
+ *waiter = NULL;
+ return ETCH_ERROR;
+ }
+
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ apr_status = apr_thread_cond_create(&apr_cond, g_etch_main_pool);
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+ if(apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ apr_thread_mutex_destroy(apr_mutex);
+ etch_free(newwaiter);
+ *waiter = NULL;
+ return ETCH_ERROR;
+ }
+
+ newwaiter->mutex = apr_mutex;
+ newwaiter->cond = apr_cond;
+ newwaiter->cond_var = 0;
+ *waiter = newwaiter;
+ return ETCH_SUCCESS;
+}
+
+etch_status_t etch_wait_set(etch_wait_t* waiter, int64 cond_value)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+ apr_thread_cond_t* apr_cond = NULL;
+
+ if(waiter == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ apr_mutex = waiter->mutex;
+ ETCH_ASSERT(apr_mutex != NULL);
+ apr_cond = waiter->cond;
+ ETCH_ASSERT(apr_cond != NULL);
+
+ apr_thread_mutex_lock(apr_mutex);
+
+ waiter->cond_var = cond_value;
+ apr_status = apr_thread_cond_broadcast(apr_cond);
+ if(apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ status = ETCH_ERROR;
+ }
+
+ apr_thread_mutex_unlock(apr_mutex);
+
+ return status;
+}
+
+etch_status_t etch_wait_reset(etch_wait_t* waiter, int64 cond_value)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+
+ if(waiter == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ apr_mutex = waiter->mutex;
+ ETCH_ASSERT(apr_mutex != NULL);
+
+ apr_thread_mutex_lock(apr_mutex);
+ waiter->cond_var = cond_value;
+ apr_thread_mutex_unlock(apr_mutex);
+
+ return status;
+}
+
+etch_status_t etch_wait_wait_helper(etch_wait_t* waiter, int64 cond_value, int64* new_cond_value)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+ apr_thread_cond_t* apr_cond = NULL;
+
+ if(waiter == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ apr_mutex = waiter->mutex;
+ ETCH_ASSERT(apr_mutex != NULL);
+ apr_cond = waiter->cond;
+ ETCH_ASSERT(apr_cond != NULL);
+
+ apr_thread_mutex_lock(apr_mutex);
+
+ while(waiter->cond_var != cond_value) {
+ apr_status = apr_thread_cond_wait(apr_cond, apr_mutex);
+ if(apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ status = ETCH_ERROR;
+ break;
+ }
+ }
+
+ if(status == ETCH_SUCCESS && new_cond_value != NULL) {
+ waiter->cond_var = *new_cond_value;
+ apr_status = apr_thread_cond_broadcast(apr_cond);
+ if(apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ }
+ }
+
+ apr_thread_mutex_unlock(apr_mutex);
+
+ return status;
+}
+
+etch_status_t etch_wait_wait(etch_wait_t* waiter, int64 cond_value)
+{
+ etch_status_t status;
+ status = etch_wait_wait_helper(waiter, cond_value, NULL);
+ return status;
+}
+
+etch_status_t etch_wait_wait_and_set(etch_wait_t* waiter, int64 cond_value, int64 new_cond_value)
+{
+ etch_status_t status;
+ status = etch_wait_wait_helper(waiter, cond_value, &new_cond_value);
+ return status;
+}
+
+static etch_status_t etch_wait_timedwait_helper(etch_wait_t* waiter, int64 cond_value, uint32 timeout, int64* new_cond_value)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+ apr_thread_cond_t* apr_cond = NULL;
+ apr_time_t now = 0;
+ apr_time_t end = 0;
+ apr_time_t diff = 0;
+
+ if(waiter == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ apr_mutex = waiter->mutex;
+ ETCH_ASSERT(apr_mutex != NULL);
+ apr_cond = waiter->cond;
+ ETCH_ASSERT(apr_cond != NULL);
+
+ now = apr_time_now();
+ end = now + timeout * 1000;
+
+ apr_thread_mutex_lock(apr_mutex);
+
+ diff = end - now;
+ while(waiter->cond_var != cond_value && diff > 0) {
+ apr_status = apr_thread_cond_timedwait(apr_cond, apr_mutex, diff);
+ if(apr_status != APR_TIMEUP && apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ status = ETCH_ERROR;
+ break;
+ }
+
+ now = apr_time_now();
+ diff = end - now;
+ }
+
+ if(status != ETCH_ERROR && waiter->cond_var != cond_value) {
+ status = ETCH_ETIMEOUT;
+ }
+
+ if(status == ETCH_SUCCESS && new_cond_value != NULL) {
+ waiter->cond_var = *new_cond_value;
+ apr_status = apr_thread_cond_broadcast(apr_cond);
+ if(apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ }
+ }
+
+ apr_thread_mutex_unlock(apr_mutex);
+
+ return status;
+}
+
+etch_status_t etch_wait_timedwait(etch_wait_t* waiter, int64 cond_value, uint32 timeout)
+{
+ etch_status_t status;
+ status = etch_wait_timedwait_helper(waiter, cond_value, timeout, NULL);
+ return status;
+}
+
+etch_status_t etch_wait_timedwait_and_set(etch_wait_t* waiter, int64 cond_value, uint32 timeout, int64 new_cond_value)
+{
+ etch_status_t status;
+ status = etch_wait_timedwait_helper(waiter, cond_value, timeout, &new_cond_value);
+ return status;
+}
+
+etch_status_t etch_wait_destroy(etch_wait_t* waiter)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+ apr_thread_cond_t* apr_cond = NULL;
+
+ if(waiter == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ apr_mutex = waiter->mutex;
+ ETCH_ASSERT(apr_mutex != NULL);
+ apr_cond = waiter->cond;
+ ETCH_ASSERT(apr_cond != NULL);
+
+
+ if (!is_etchobj_static_content(waiter)) {
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ apr_status = apr_thread_cond_destroy(apr_cond);
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+ if(apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ status = ETCH_ERROR;
+ }
+
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ apr_status = apr_thread_mutex_destroy(apr_mutex);
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+ if(apr_status != APR_SUCCESS) {
+ char temp[1024];
+ apr_strerror(apr_status, temp, sizeof(temp));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", temp);
+ status = ETCH_ERROR;
+ }
+
+ if(!is_etchobj_static_shell(waiter)) {
+ etch_free(waiter);
+ }
+ }
+ return status;
+}
diff --git a/binding-c/runtime/c/src/main/etch-c.vcproj b/binding-c/runtime/c/src/main/etch-c.vcproj
new file mode 100644
index 0000000..ed306e2
--- /dev/null
+++ b/binding-c/runtime/c/src/main/etch-c.vcproj
@@ -0,0 +1,890 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="etch-c"
+ ProjectGUID="{C49054D6-0122-4F20-A0A2-06197D08567B}"
+ RootNamespace="etchc"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)target/win32/$(ConfigurationName)"
+ IntermediateDirectory="$(SolutionDir)target/win32/$(ConfigurationName)"
+ ConfigurationType="4"
+ InheritedPropertySheets=".\etch-c.vsprops"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\include;..\extern\jenkhash;"$(APR)\include";"$(APR_ICONV)\include""
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ ProgramDataBaseFileName="$(IntDir)\etch-c.pdb"
+ WarningLevel="3"
+ WarnAsError="true"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ LinkLibraryDependencies="true"
+ AdditionalLibraryDirectories=""
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)target/win32/$(ConfigurationName)"
+ IntermediateDirectory="$(SolutionDir)target/win32/$(ConfigurationName)"
+ ConfigurationType="4"
+ InheritedPropertySheets=".\etch-c.vsprops"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\include;..\extern\jenkhash;"$(APR)\include";"$(APR_ICONV)\include""
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ ProgramDataBaseFileName="$(IntDir)\etch-c.pdb"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Static|Win32"
+ OutputDirectory="$(SolutionDir)target/win32/$(ConfigurationName)"
+ IntermediateDirectory="$(SolutionDir)target/win32/$(ConfigurationName)"
+ ConfigurationType="4"
+ InheritedPropertySheets=".\etch-c.vsprops"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\include;..\extern\jenkhash;"$(APR)\include\win32_x86";"$(APR)\include";"$(APR_ICONV)\include""
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB;APR_DECLARE_STATIC;API_DECLARE_STATIC"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ WarningLevel="3"
+ WarnAsError="true"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release Static|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ InheritedPropertySheets=".\etch-c.vsprops"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Quelldateien"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <Filter
+ Name="common"
+ >
+ <File
+ RelativePath=".\common\etch_arraylist.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_arrayval.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_cache.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_collection.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_config.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_encoding.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_exception.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_flexbuffer.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_general.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_hash.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_hashfunc.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_linked_list.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_log.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_map.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_mem.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_mutex.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_nativearray.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_object.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_object_types.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_runtime.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_thread.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_thread2.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_threadpool.c"
+ >
+ </File>
+ <File
+ RelativePath=".\apr\etch_threadpool_apr.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_url.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\etch_wait.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="internal"
+ >
+ <File
+ RelativePath=".\internal\etchinternal_single_linked_list.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="message"
+ >
+ <File
+ RelativePath=".\bindings\msg\etch_default_value_factory.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_field.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_id_name.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_message.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_structval.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_tagdata.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_type.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_validator.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_value_factory.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="transport"
+ >
+ <File
+ RelativePath=".\bindings\msg\etch_binary_tdi.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_binary_tdo.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_connection.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_connection_event.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_mailbox_manager.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\etch_mailboxint.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_messagizer.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_packetizer.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_plain_mailbox.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_plain_mailbox_manager.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_session_data.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_session_listener.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_session_message.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_session_packet.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\etch_sessionint.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\etch_simpletimer.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\etch_sourceint.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_tagdata_inp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\msg\etch_tagdata_out.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_tcp_connection.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_tcp_server.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_tdformat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_transport.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_transport_data.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_transport_message.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\etch_transport_packet.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\etch_transportint.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="support"
+ >
+ <File
+ RelativePath=".\transport\etch_queue.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\etch_queue_apr.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\etch_remote.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bindings\support\etch_resources.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\etch_serializer.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\etch_stub.c"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Headerdateien"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\..\include\etch.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_arraylist.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_arrayval.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_binary_tdi.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_binary_tdo.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_cache.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_collection.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_config.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_connection.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_default_value_factory.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_encoding.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_errno.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_exception.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_field.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_flexbuffer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_general.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_hash.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_id_name.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_id_name_map.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_linked_list.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_log.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_mailbox.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_mailbox_manager.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_map.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_mem.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_message.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_messagizer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_msgutl.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_mutex.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_nativearray.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_object.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_objecttypes.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_packetizer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_plain_mailbox.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_plain_mailbox_manager.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_queue.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_queue_apr.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_remote.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_resources.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_runtime.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_serializer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_session_data.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_session_listener.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_session_message.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_session_packet.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_sessionint.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_simpletimer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_sourceint.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_structval.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_stub.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_svcobj_masks.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_tagdata_inp.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_tagdata_out.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_tagged_data.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_tcp_connection.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_tcp_server.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_tdformat.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_thread.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_thread2.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_threadpool.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_threadpool_apr.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_transport.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_transport_data.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_transport_message.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_transport_packet.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_transportint.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_type.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_url.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_validator.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_value_factory.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_wait.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etch_warn.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\include\etchinternal_single_linked_list.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Ressourcendateien"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath="..\..\README.TXT"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/binding-c/runtime/c/src/main/etch-c.vsprops b/binding-c/runtime/c/src/main/etch-c.vsprops
new file mode 100644
index 0000000..2cd7374
--- /dev/null
+++ b/binding-c/runtime/c/src/main/etch-c.vsprops
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="etch-c"
+ >
+ <UserMacro
+ Name="APR"
+ Value="$(SolutionDir)src\extern\apr\apr"
+ PerformEnvironmentSet="true"
+ />
+ <UserMacro
+ Name="APR_ICONV"
+ Value="$(SolutionDir)src\extern\apr\apr-iconv"
+ PerformEnvironmentSet="true"
+ />
+</VisualStudioPropertySheet>
diff --git a/binding-c/runtime/c/src/main/internal/etchinternal_single_linked_list.c b/binding-c/runtime/c/src/main/internal/etchinternal_single_linked_list.c
new file mode 100644
index 0000000..1e70b81
--- /dev/null
+++ b/binding-c/runtime/c/src/main/internal/etchinternal_single_linked_list.c
@@ -0,0 +1,75 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "etchinternal_single_linked_list.h"
+
+#include <string.h>
+#include "etch_mem.h"
+
+
+etchinternal_single_linked_list_node* etchinternal_single_linked_list_node_create(void* data, etchinternal_single_linked_list_node* next) {
+ etchinternal_single_linked_list_node* res = (etchinternal_single_linked_list_node*)etch_malloc(sizeof(etchinternal_single_linked_list_node), 0);
+ res->data = data;
+ res->next = next;
+
+ return res;
+}
+
+etchinternal_single_linked_list* etchinternal_single_linked_list_create() {
+ etchinternal_single_linked_list* res = (etchinternal_single_linked_list*)etch_malloc(sizeof(etchinternal_single_linked_list), 0);
+ memset(res, 0, sizeof(etchinternal_single_linked_list));
+ return res;
+}
+
+
+void etchinternal_single_linked_list_destroy(etchinternal_single_linked_list* list) {
+ etchinternal_single_linked_list_node* help = list->head;
+ etchinternal_single_linked_list_node* help2;
+ while (help) {
+ help2 = help->next;
+
+ free(help->data);
+ free(help);
+
+ help = help2;
+ }
+ free(list);
+}
+
+void etchinternal_single_linked_list_add(etchinternal_single_linked_list* list, void* data, size_t size) {
+ void* new_data = etch_malloc(size, 0);
+ etchinternal_single_linked_list_node* new_node;
+ memcpy(new_data, data, size);
+ new_node = etchinternal_single_linked_list_node_create(new_data, list->head);
+ list->head = new_node;
+}
+
+void* etchinternal_single_linked_list_find(etchinternal_single_linked_list* list, etchinternal_find_func find, void* find_data) {
+ etchinternal_single_linked_list_node* help = list->head;
+ while (help) {
+ if (find(help->data, find_data)) {
+ return help->data;
+ }
+
+ help = help->next;
+ }
+ return 0;
+}
diff --git a/binding-c/runtime/c/src/main/support/etch_mailboxint.c b/binding-c/runtime/c/src/main/support/etch_mailboxint.c
new file mode 100644
index 0000000..eedb053
--- /dev/null
+++ b/binding-c/runtime/c/src/main/support/etch_mailboxint.c
@@ -0,0 +1,264 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_mailboxint.c
+ * mailbox interface
+ */
+
+#include "etch_mailbox.h"
+#include "etch_plain_mailbox.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+
+/*
+static const char* LOG_CATEGORY = "etch_mailbox";
+*/
+
+int etchmbi_def_message(void* thisx, etch_who* whofrom, etch_message* msg);
+int etchmbi_def_read (void* thisx, etch_mailbox_element** out);
+int etchmbi_def_read_withwait (void* thisx, const int maxdelay, etch_mailbox_element** out);
+int etchmbi_def_close_delivery (void* thisx);
+int etchmbi_def_close_read (void* thisx);
+int etchmbi_def_register_notify (void* thisx, etch_mailbox_notify, etch_object* state, const int maxdelay);
+int etchmbi_def_unregister_notify (void* thisx, etch_mailbox_notify);
+int etchmbi_def_is_empty (void* thisx);
+int etchmbi_def_is_closed (void* thisx);
+int etchmbi_def_is_full (void* thisx);
+int64 etchmbi_def_get_message_id (void* thisx);
+int destroy_mailbox_interface(void*);
+
+
+/**
+ * new_default_mailbox_interface
+ * return a mailbox interface populated with defaults for virtuals.
+ */
+i_mailbox* new_default_mailbox_interface(void* thisx)
+{
+ i_mailbox* newi = (i_mailbox*) etch_malloc(sizeof(i_mailbox), ETCHTYPEB_RAWOBJECT);
+
+ newi->thisx = thisx;
+ newi->destroy = destroy_mailbox_interface;
+ newi->mailbox = etchmbox_get_implobj;
+ newi->manager = etchmbox_get_manager;
+
+ newi->message = etchmbi_def_message;
+ newi->read = etchmbi_def_read;
+ newi->read_withwait = etchmbi_def_read_withwait;
+ newi->close_delivery = etchmbi_def_close_delivery;
+ newi->close_read = etchmbi_def_close_read;
+ newi->register_notify = etchmbi_def_register_notify;
+ newi->unregister_notify = etchmbi_def_unregister_notify;
+ newi->is_empty = etchmbi_def_is_empty;
+ newi->is_closed = etchmbi_def_is_closed;
+ newi->is_full = etchmbi_def_is_full;
+ newi->get_message_id = etchmbi_def_get_message_id;
+
+ return newi;
+}
+
+
+/**
+ * new_mailbox_interface
+ * return a mailbox interface populated with specified virtuals.
+ */
+i_mailbox* new_mailbox_interface(void* thisx,
+ etch_mailbox_message mbm,
+ etch_mailbox_read mbr,
+ etch_mailbox_read_withwait mbrd,
+ etch_mailbox_close_delivery mbcd,
+ etch_mailbox_close_read mbcr,
+ etch_mailbox_register_notify mbrn,
+ etch_mailbox_unregister_notify mbun,
+ etch_mailbox_is_empty mbise,
+ etch_mailbox_is_closed mbisc,
+ etch_mailbox_is_full mbisf,
+ etch_mailbox_get_message_id mbgmi)
+{
+ i_mailbox* newi = (i_mailbox*) new_object
+ (sizeof(i_mailbox), ETCHTYPEB_MAILBOXINT, CLASSID_MAILBOXINT);
+
+ newi->thisx = thisx;
+ newi->destroy = destroy_mailbox_interface;
+ newi->mailbox = etchmbox_get_implobj;
+ newi->manager = etchmbox_get_manager;
+
+ newi->message = mbm? mbm: etchmbi_def_message;
+ newi->read = mbr? mbr: etchmbi_def_read;
+ newi->read_withwait = mbrd? mbrd: etchmbi_def_read_withwait;
+ newi->close_delivery = mbcd? mbcd: etchmbi_def_close_delivery;
+ newi->close_read = mbcr? mbcr: etchmbi_def_close_read;
+ newi->register_notify = mbrn? mbrn: etchmbi_def_register_notify;
+ newi->unregister_notify = mbun? mbun: etchmbi_def_unregister_notify;
+ newi->is_empty = mbise? mbise: etchmbi_def_is_empty;
+ newi->is_closed = mbisc? mbisc: etchmbi_def_is_closed;
+ newi->is_full = mbisf? mbisf: etchmbi_def_is_full;
+ newi->get_message_id = mbgmi? mbgmi: etchmbi_def_get_message_id;
+
+ return newi;
+}
+
+
+/**
+ * destroy_mailbox_interface()
+ * i_mailbox destructor
+ */
+int destroy_mailbox_interface (void* data)
+{
+ i_mailbox* mb = (i_mailbox*)data;
+ int result = 0;
+ if (mb && !is_etchobj_static_content(mb))
+ {
+ i_mailbox* temp = mb->thisx; //destroying thisx will also destroy mb...
+ // ETCHOBJ_DESTROY();
+ if(temp)
+ ((etch_object*)temp)->destroy((etch_object*)temp);
+ temp = NULL;
+
+ }
+ else result = -1;
+ return result;
+}
+
+
+/**
+ * etch_mailbox_get_implobj()
+ * verify and return the concrete mailbox implementation
+ */
+etch_mailbox* etchmbox_get_implobj(i_mailbox* imb)
+{
+ etch_mailbox* mbox = imb? imb->thisx: NULL;
+ return is_etch_mailbox(mbox)? mbox: NULL;
+}
+
+
+/**
+ * etch_mailbox_get_manager()
+ * verify and return the mailbox's mailbox manager
+ */
+i_mailbox_manager* etchmbox_get_manager(i_mailbox* imb)
+{
+ etch_mailbox* mbox = imb? imb->thisx: NULL;
+ i_mailbox_manager* mgr = mbox? mbox->manager: NULL;
+ return is_etch_mailboxmgr(mgr)? mgr: NULL;
+}
+
+
+int etchmbi_def_message(void* thisx, etch_who* whofrom, etch_message* msg)
+{
+ return -1;
+}
+
+
+int etchmbi_def_read (void* thisx, etch_mailbox_element** out)
+{
+ return -1;
+}
+
+
+int etchmbi_def_read_withwait (void* thisx, const int maxdelay, etch_mailbox_element** out)
+{
+ return -1;
+}
+
+
+int etchmbi_def_close_delivery (void* thisx)
+{
+ return -1;
+}
+
+
+int etchmbi_def_close_read (void* thisx)
+{
+ return -1;
+}
+
+
+int etchmbi_def_register_notify (void* thisx, etch_mailbox_notify fn, etch_object* state, const int maxdelay)
+{
+ return -1;
+}
+
+int etchmbi_def_unregister_notify (void* thisx, etch_mailbox_notify fn)
+{
+ return -1;
+}
+
+
+int etchmbi_def_is_empty (void* thisx)
+{
+ return FALSE;
+}
+
+
+int etchmbi_def_is_closed (void* thisx)
+{
+ return FALSE;
+}
+
+
+int etchmbi_def_is_full (void* thisx)
+{
+ return FALSE;
+}
+
+
+int64 etchmbi_def_get_message_id (void* thisx)
+{
+ return 0;
+}
+
+
+/* - - - - - - - - - - - - - - - -
+ * etch_mailbox_element
+ * - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * destroy_mailbox_element()
+ * etch_mailbox_element destructor
+ */
+int destroy_mailbox_element(void* data)
+{
+ etch_mailbox_element* thisx = (etch_mailbox_element*)data;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ etch_object_destroy(thisx->msg);
+ thisx->msg = NULL;
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * new_mailbox_element()
+ * etch_mailbox_element constructor
+ * @param msg todo document ownership
+ * @param whofrom todo document ownership
+ */
+etch_mailbox_element* new_mailbox_element(etch_message* msg, etch_who* who)
+{
+ etch_mailbox_element* elt = (etch_mailbox_element*) new_object
+ (sizeof(etch_mailbox_element), ETCHTYPEB_MBOX_ELEMENT, CLASSID_MBOX_ELEMENT);
+ elt->msg = msg;
+ elt->whofrom = who;
+ elt->destroy = destroy_mailbox_element;
+ return elt;
+}
diff --git a/binding-c/runtime/c/src/main/support/etch_queue_apr.c b/binding-c/runtime/c/src/main/support/etch_queue_apr.c
new file mode 100644
index 0000000..07bbed3
--- /dev/null
+++ b/binding-c/runtime/c/src/main/support/etch_queue_apr.c
@@ -0,0 +1,441 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_queue_apr.c
+ * based on apr_queue, modified by Cisco as follows:
+ * timeouts were added on the push and pop waits, and functions were modified
+ * to release their lock at a single exit point.
+ */
+
+#include "apr.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+//#include "apu.h"
+#include "apr_portable.h"
+#include "apr_thread_mutex.h"
+#include "apr_thread_cond.h"
+#include "apr_errno.h"
+
+#include "etch_queue_apr.h" /* Cisco */
+
+/* #define QUEUE_DEBUG */
+
+#ifdef QUEUE_DEBUG
+static void Q_DBG(char*msg, etch_apr_queue_t *q) {
+ fprintf(stderr, "%ld\t#%d in %d out %d\t%s\n",
+ (size_t) apr_os_thread_current(), /* cast - Cisco */
+ q->nelts, q->in, q->out,
+ msg
+ );
+ fflush(stdout); /* Cisco */
+}
+#else
+#define Q_DBG(x,y)
+#endif
+
+
+/**
+ * Detects when the etch_apr_queue_t is full. This utility function is expected
+ * to be called from within critical sections, and is not threadsafe.
+ */
+#define etch_apr_queue_full(queue) ((queue)->nelts == (queue)->bounds)
+
+
+/**
+ * Detects when the etch_apr_queue_t is empty. This utility function is expected
+ * to be called from within critical sections, and is not threadsafe.
+ */
+#define etch_apr_queue_empty(queue) ((queue)->nelts == 0)
+
+
+/**
+ * Callback routine that is called to destroy this
+ * etch_apr_queue_t when its pool is destroyed.
+ */
+static apr_status_t etch_apr_queue_destroy(void *data)
+{
+ etch_apr_queue_t *queue = data;
+
+ apr_thread_cond_destroy(queue->not_empty);
+ apr_thread_cond_destroy(queue->not_full);
+ apr_thread_mutex_destroy(queue->one_big_mutex);
+
+ return APR_SUCCESS;
+}
+
+
+/**
+ * Initialize the etch_apr_queue_t.
+ */
+apr_status_t etch_apr_queue_create(etch_apr_queue_t **q,
+ unsigned int queue_capacity,
+ apr_pool_t *a)
+{
+ apr_status_t rv;
+ etch_apr_queue_t *queue;
+ queue = apr_palloc(a, sizeof(etch_apr_queue_t));
+ *q = queue;
+
+ /* nested doesn't work ;( */
+ rv = apr_thread_mutex_create(&queue->one_big_mutex,
+ APR_THREAD_MUTEX_UNNESTED,
+ a);
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ rv = apr_thread_cond_create(&queue->not_empty, a);
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ rv = apr_thread_cond_create(&queue->not_full, a);
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ /* Set all the data in the queue to NULL */
+ queue->data = apr_pcalloc(a, queue_capacity * sizeof(void*));
+ queue->bounds = queue_capacity;
+ queue->nelts = 0;
+ queue->in = 0;
+ queue->out = 0;
+ queue->terminated = 0;
+ queue->full_waiters = 0;
+ queue->empty_waiters = 0;
+
+ apr_pool_cleanup_register(a, queue, etch_apr_queue_destroy, apr_pool_cleanup_null);
+
+ return APR_SUCCESS;
+}
+
+
+/**
+ * Push new data onto the queue. Blocks if the queue is full. Once
+ * the push operation has completed, it signals other threads waiting
+ * in apr_queue_pop() that they may continue consuming sockets.
+ * @param timeout added by Cisco. now uses apr_thread_cond_timewait().
+ * interval of time to wait. zero means forever, negative indicates no wait,
+ * otherwise wait time in *microseconds*.
+ * @return APR_SUCCESS, APR_EAGAIN, APR_EOF, APR_EINTR, APR_TIMEUP,
+ * or some APR error
+ */
+apr_status_t etch_apr_queue_push(etch_apr_queue_t *queue,
+ apr_interval_time_t timeout,
+ void *data)
+{
+ apr_status_t rv;
+
+ if (queue->terminated)
+ rv = APR_EOF; /* no more elements ever again */
+ else
+ if (APR_SUCCESS == (rv = apr_thread_mutex_lock(queue->one_big_mutex)))
+ {
+ do
+ { if (etch_apr_queue_full(queue))
+ {
+ if (!queue->terminated)
+ {
+ if (-1 == timeout)
+ { rv = APR_EAGAIN; /* asked to not wait */
+ break;
+ }
+
+ queue->full_waiters++;
+
+ if (0 == timeout)
+ rv = apr_thread_cond_wait(queue->not_full, queue->one_big_mutex);
+ else
+ rv = apr_thread_cond_timedwait(queue->not_full, queue->one_big_mutex, timeout);
+
+ queue->full_waiters--;
+ if (rv != APR_SUCCESS)
+ break;
+ }
+
+ /* If we wake up and it's still empty, then we were interrupted */
+ if (etch_apr_queue_full(queue))
+ {
+ Q_DBG("queue full (intr)", queue);
+ rv = queue->terminated? APR_EOF: APR_EINTR;
+ break;
+ }
+ }
+
+ queue->data[queue->in] = data;
+ queue->in = (queue->in + 1) % queue->bounds;
+ queue->nelts++;
+
+ if (queue->empty_waiters)
+ {
+ Q_DBG("sig !empty", queue);
+ rv = apr_thread_cond_signal(queue->not_empty);
+ }
+
+ } while(0);
+
+ apr_thread_mutex_unlock(queue->one_big_mutex);
+ }
+
+ return rv;
+}
+
+
+/**
+ * Push new data onto the queue. Blocks if the queue is full. Once
+ * the push operation has completed, it signals other threads waiting
+ * in apr_queue_pop() that they may continue consuming sockets.
+ */
+apr_status_t etch_apr_queue_trypush(etch_apr_queue_t *queue, void *data)
+{
+ apr_status_t rv;
+
+ if (queue->terminated)
+ rv = APR_EOF;
+ else
+ if (APR_SUCCESS == (rv = apr_thread_mutex_lock(queue->one_big_mutex)))
+ {
+ if (etch_apr_queue_full(queue))
+ rv = APR_EAGAIN;
+ else
+ { queue->data[queue->in] = data;
+ queue->in = (queue->in + 1) % queue->bounds;
+ queue->nelts++;
+
+ if (queue->empty_waiters)
+ {
+ Q_DBG("sig !empty", queue);
+ rv = apr_thread_cond_signal(queue->not_empty);
+ }
+ }
+
+ apr_thread_mutex_unlock(queue->one_big_mutex);
+ }
+
+ return rv;
+}
+
+
+/**
+ * not thread safe
+ */
+unsigned int etch_apr_queue_size(etch_apr_queue_t *queue) {
+ return queue->nelts;
+}
+
+
+/**
+ * Retrieves the next item from the queue. If there are no
+ * items available, it will block until one becomes available.
+ * Once retrieved, the item is placed into the address specified by
+ * 'data'.
+ * @param timeout added by Cisco. now uses apr_thread_cond_timewait().
+ * interval of time to wait. zero means forever, -1 means no wait,
+ * -2 means don't wait and ignore queue closed indicator,
+ * otherwise timeout is blocking time in microseconds.
+ * @return APR_SUCCESS, APR_EAGAIN, APR_EOF, APR_EINTR, APR_TIMEUP,
+ * or some APR error
+ */
+apr_status_t etch_apr_queue_pop(etch_apr_queue_t *queue,
+ apr_interval_time_t timeout,
+ void **data)
+{
+ apr_status_t rv;
+
+ if (queue->terminated) /* Cisco back door to clear closed queue */
+ { if (timeout != ETCHQUEUE_CLEARING_CLOSED_QUEUE)
+ return APR_EOF; /* no more elements ever again */
+ }
+
+ if (APR_SUCCESS == (rv = apr_thread_mutex_lock(queue->one_big_mutex)))
+ {
+ do
+ { /* Keep waiting until we wake up and find that the queue is not empty. */
+ if (etch_apr_queue_empty(queue))
+ {
+ if (-1 == timeout)
+ { rv = APR_EAGAIN; /* asked to not wait */
+ break;
+ }
+
+ if (!queue->terminated)
+ {
+ queue->empty_waiters++;
+
+ if (0 == timeout)
+ rv = apr_thread_cond_wait(queue->not_empty, queue->one_big_mutex);
+ else
+ rv = apr_thread_cond_timedwait(queue->not_empty, queue->one_big_mutex, timeout);
+
+ queue->empty_waiters--;
+
+ if (rv != APR_SUCCESS) /* rv will be APR_TIMEUP if timed out */
+ break;
+ }
+
+ /* If we wake up and it's still empty, then we were interrupted */
+ if (etch_apr_queue_empty(queue))
+ {
+ Q_DBG("queue empty (intr)", queue);
+ rv = queue->terminated? APR_EOF: APR_EINTR;
+ break;
+ }
+ }
+
+ *data = queue->data[queue->out];
+ queue->nelts--;
+
+ queue->out = (queue->out + 1) % queue->bounds;
+
+ if (queue->full_waiters)
+ {
+ Q_DBG("signal !full", queue);
+ rv = apr_thread_cond_signal(queue->not_full);
+ }
+
+ } while(0);
+
+ apr_thread_mutex_unlock(queue->one_big_mutex);
+ }
+
+ return rv;
+}
+
+
+/**
+ * Retrieves the next item from the queue. If there are no
+ * items available, return APR_EAGAIN. Once retrieved,
+ * the item is placed into the address specified by 'data'.
+ */
+apr_status_t etch_apr_queue_trypop(etch_apr_queue_t *queue, void **data)
+{
+ apr_status_t rv;
+
+ if (queue->terminated)
+ rv = APR_EOF; /* no more elements ever again */
+ else
+ if (APR_SUCCESS == (rv = apr_thread_mutex_lock(queue->one_big_mutex)))
+ {
+ if (etch_apr_queue_empty(queue))
+ rv = APR_EAGAIN;
+ else
+ { *data = queue->data[queue->out];
+ queue->nelts--;
+
+ queue->out = (queue->out + 1) % queue->bounds;
+
+ if (queue->full_waiters)
+ {
+ Q_DBG("signal !full", queue);
+ rv = apr_thread_cond_signal(queue->not_full);
+ }
+ }
+
+ apr_thread_mutex_unlock(queue->one_big_mutex);
+ }
+
+ return rv;
+}
+
+
+apr_status_t etch_apr_queue_interrupt_all(etch_apr_queue_t *queue)
+{
+ apr_status_t rv;
+ Q_DBG("intr all", queue);
+
+ if (APR_SUCCESS == (rv = apr_thread_mutex_lock(queue->one_big_mutex)))
+ {
+ apr_thread_cond_broadcast(queue->not_empty);
+ apr_thread_cond_broadcast(queue->not_full);
+
+ apr_thread_mutex_unlock(queue->one_big_mutex);
+ }
+
+ return rv;
+}
+
+
+/**
+ * etch_apr_queue_unsafe_interrupt_all()
+ * added by Cisco to opeate when lock already held since queue lock is not nestable.
+ */
+apr_status_t etch_apr_queue_unsafe_interrupt_all(etch_apr_queue_t *queue)
+{
+ Q_DBG("intr all", queue);
+
+ apr_thread_cond_broadcast(queue->not_empty);
+ apr_thread_cond_broadcast(queue->not_full);
+
+ return APR_SUCCESS;
+}
+
+
+apr_status_t etch_apr_queue_term(etch_apr_queue_t *queue)
+{
+ apr_status_t rv;
+
+ if (APR_SUCCESS == (rv = apr_thread_mutex_lock(queue->one_big_mutex)))
+ {
+ /* we must hold one_big_mutex when setting this... otherwise,
+ * we could end up setting it and waking everybody up just after a
+ * would-be popper checks it but right before they block
+ */
+ queue->terminated = 1;
+
+ apr_thread_mutex_unlock(queue->one_big_mutex);
+ }
+
+ return etch_apr_queue_interrupt_all(queue);
+}
+
+
+/**
+ * added by Cisco to close queue when lock already held
+ */
+apr_status_t etch_apr_queue_unsafeclose(etch_apr_queue_t *queue)
+{
+ queue->terminated = 1;
+ return etch_apr_queue_unsafe_interrupt_all(queue);
+}
+
+
+/**
+ * added by Cisco to access lock externally
+ */
+apr_status_t etch_apr_queue_lock(etch_apr_queue_t *queue)
+{
+ return apr_thread_mutex_lock(queue->one_big_mutex);
+}
+
+
+/**
+ * added by Cisco to access lock externally
+ */
+apr_status_t etch_apr_queue_unlock(etch_apr_queue_t *queue)
+{
+ return apr_thread_mutex_unlock(queue->one_big_mutex);
+}
+
+
+/**
+ * etch_apr_queue_trylock()
+ * added by Cisco to access lock externally
+ */
+int etch_apr_queue_trylock(etch_apr_queue_t *queue)
+{
+ return apr_thread_mutex_trylock(queue->one_big_mutex);
+}
diff --git a/binding-c/runtime/c/src/main/support/etch_remote.c b/binding-c/runtime/c/src/main/support/etch_remote.c
new file mode 100644
index 0000000..33bf0b8
--- /dev/null
+++ b/binding-c/runtime/c/src/main/support/etch_remote.c
@@ -0,0 +1,330 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_remote.c
+ * base methods for either type of remote
+ */
+
+#include "etch_remote.h"
+#include "etch_exception.h"
+#include "etch_url.h"
+#include "etch_log.h"
+#include "etch_objecttypes.h"
+
+static const char* LOG_CATEGORY = "etch_remote";
+
+/**
+ * is_etch_remote()
+ */
+int is_etch_remote(void* x)
+{
+ int result = FALSE, objtype = x? ((etch_object*)x)->obj_type: 0;
+ switch(objtype)
+ { case ETCHTYPEB_REMOTE: case ETCHTYPEB_REMOTECLIENT: case ETCHTYPEB_REMOTESERVER:
+ result = TRUE;
+ }
+ return result;
+}
+
+
+/**
+ * etchremote_new_message()
+ * instantiates a message to be sent via this.send() or this.begin_call().
+ * @param thisx this remote object.
+ * @param message_type type of message, caller retains.
+ * @return message object, or an etch object wrapping an exception.
+ */
+etch_message* etchremote_new_message (void* data, etch_type* message_type)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ etch_message* msg = new_message (message_type, ETCH_DEFSIZE, thisx->vf);
+
+ /* removed this throw because caller (remote) can't use the exception
+ * without rethrowing it to the mailbox object */
+ #if(0)
+ if (NULL == msg) /* warning this is not a usable message object */
+ msg = (etch_message*) throw_from(EXCPTYPE_ETCHRUNTIME, ETCHTYPEB_MESSAGE,
+ L"could not create message", ETCHEXCP_COPYTEXT);
+ #endif
+
+ return msg;
+}
+
+
+/**
+ * etchremote_send()
+ * sends message to recipient without waiting for a response.
+ * @param thisx this remote object.
+ * @param msg message, caller relinquishes.
+ * @return 0 success, -1 failure.
+ */
+int etchremote_send (void* data, etch_message* msg)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ int result = 0;
+ ETCH_ASSERT(is_etch_remote(thisx));
+
+ result = thisx->dsvc->itm->transport_message(thisx->dsvc, NULL, msg);
+
+ if (0 != result)
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "remote server send failed for msg %x\n", msg);
+
+ return result;
+}
+
+
+/**
+ * etchremote_sendex()
+ * sends message to recipient without waiting for a response.
+ * @param thisx this remote object.
+ * @param msg message, caller relinquishes.
+ * @return NULL or exception
+ */
+void* etchremote_sendex (void* data, etch_message* msg)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ etch_object* resultobj = NULL;
+
+ if (0 != etchremote_send (thisx, msg)){
+ etch_object_destroy(resultobj);
+ resultobj = (etch_object*)new_etch_exception_from_errorcode(ETCH_EIO);
+
+ etch_exception_set_message((etch_exception*)resultobj,new_stringw(L"remote server send failed"));
+ }
+
+ return resultobj;
+}
+
+
+/**
+ * etchremote_begincall()
+ * sends message beginning a call sequence.
+ * @param thisx this remote object.
+ * @param msg message, caller relinquishes.
+ * @return in out parameter, a mailbox which can be used to retrieve the response.
+ * @return 0 success, -1 failure.
+ */
+int etchremote_begincall (void* data, etch_message* msg, void** out)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ int result = 0;
+ ETCH_ASSERT(is_etch_remote(thisx));
+
+ result = thisx->dsvc->begin_call(thisx->dsvc, msg, out);
+
+ if (0 != result)
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not create mailbox for msg %x\n", msg);
+
+ return result;
+}
+
+
+/**
+ * etchremote_endcall()
+ * finishes a call sequence by waiting for a response message.
+ * @param thisx this remote object.
+ * @param mbox a mailbox which will be used to read an expected message response.
+ * @param response_type the message type of the expected response.
+ * @return in out parameter, on success, the response etch_object* masked object.
+ * @return 0 success, -1 failure.
+ */
+int etchremote_endcall (void* data, i_mailbox* mbox, etch_type* response_type, void** out)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ ETCH_ASSERT(is_etch_remote(thisx));
+
+ return thisx->dsvc->end_call(thisx->dsvc, mbox, response_type, out);
+}
+
+
+/**
+ * etchremote_transport_control()
+ * @param evt caller relinquishes
+ * @param value caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+int etchremote_transport_control (void* data, etch_event* evt, etch_object* value)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ ETCH_ASSERT(is_etch_remote(thisx));
+
+ return thisx->dsvc->itm->transport_control(thisx->dsvc, evt, value);
+}
+
+
+/**
+ * etchremote_transport_notify()
+ * @param evt caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+int etchremote_transport_notify (void* data, etch_event* evt)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ ETCH_ASSERT(is_etch_remote(thisx));
+
+ return thisx->dsvc->itm->transport_notify(thisx->dsvc, evt);
+}
+
+
+/**
+ * etchremote_transport_query()
+ * @param query caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+etch_object* etchremote_transport_query (void* data, etch_query* query)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ ETCH_ASSERT(is_etch_remote(thisx));
+
+ return thisx->dsvc->itm->transport_query(thisx->dsvc, query);
+}
+
+
+/**
+ * etchremote_set_session()
+ * @param session caller retains
+ * @return 0 success, -1 failure.
+ */
+void etchremote_set_session (void* data, void* session)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ ETCH_ASSERT(is_etch_remote(thisx));
+}
+
+
+/**
+ * etchremote_get_session()
+ * @param session caller retains
+ * @return 0 success, -1 failure.
+ */
+i_session* etchremote_get_session (void* data)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ ETCH_ASSERT(is_etch_remote(thisx));
+ return (i_session*)thisx->dsvc->ism;
+}
+
+
+/**
+ * etchremote_start_waitup()
+ * start the transport and wait for it to come up.
+ * @param thisx this remote object.
+ * @param waitms how long to wait, in milliseconds.
+ * @return 0 success, -1 failure.
+ */
+int etchremote_start_waitup (void* data, const int waitms)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ ETCH_ASSERT(is_etch_remote(thisx));
+ ETCH_ASSERT(thisx->transport_control);
+
+ /* indicate to transport start whether this request is from a client
+ * (remote server) or a server (remote client) */
+ { const int is_client = thisx->remote_type == ETCH_REMOTETYPE_SERVER;
+ etch_int32* txvalue = is_client? new_int32(is_client): NULL;
+ etch_event* txevent = new_etch_event(CLASSID_CONTROL_START_WAITUP, waitms);
+
+ return thisx->transport_control (thisx, txevent, (etch_object*)txvalue);
+ }
+}
+
+
+/**
+ * etchremote_stop_waitdown()
+ * stop the transport and wait for it to go down.
+ * @param thisx this remote object.
+ * @param waitms how long to wait, in milliseconds.
+ * @return 0 success, -1 failure.
+ */
+int etchremote_stop_waitdown (void* data, const int waitms)
+{
+ xxxx_remote* thisx = (xxxx_remote*)data;
+ ETCH_ASSERT(is_etch_remote(thisx));
+ ETCH_ASSERT(thisx->transport_control);
+
+ /* indicate to transport start whether this request is from a client
+ * (remote server) or a server (remote client) */
+ { const int is_client = thisx->remote_type == ETCH_REMOTETYPE_SERVER;
+ etch_int32* txvalue = is_client? new_int32(is_client): NULL;
+ etch_event* txevent = new_etch_event(CLASSID_CONTROL_STOP_WAITDOWN, waitms);
+
+ #ifdef ETCH_SHORTCIRCUIT_CLIENT_DEMO
+ if (is_client) /* dead end chain here for client demo */
+ {
+ etch_object_destroy(txevent);
+ txevent = NULL;
+
+ etch_object_destroy(txvalue);
+ txvalue = NULL;
+
+ return -1;
+ }
+ #endif /* ETCH_SHORTCIRCUIT_CLIENT_DEMO */
+
+ return thisx->transport_control (thisx, txevent, (etch_object*)txvalue);
+ }
+}
+
+
+/**
+ * new_etch_remote_base
+ * generic constructor for remote base
+ * @param thisx parent object such as remote server or client.
+ * @param objsize byte length of actual remote base object,
+ * specifying ETCH_DEFSIZE defaults to sizeof(xxxx_remote).
+ * @param class_id etch class id of this object.
+ * @param ids delivery service -- caller retains.
+ * @param vf default value factory -- caller retains.
+ * @param ixxxx service interface -- caller retains.
+ * @return xxxx_remote* mask over either remote type.
+ * fyi xxxx_remote typedefs to etch_remote.
+ */
+xxxx_remote* new_etch_remote_base (void* thisx,
+ const int objsize, const unsigned short class_id,
+ i_delivery_service* ids, etch_value_factory* vf, etch_object* ixxxx)
+{
+ const int nbytes = objsize? objsize: sizeof(xxxx_remote);
+
+ xxxx_remote* remote = (xxxx_remote*) new_object (nbytes, ETCHTYPEB_REMOTE, class_id);
+
+ /* xxxx_remote instance data and methods */
+ remote->dsvc = ids;
+ remote->vf = vf;
+ remote->start_waitup = etchremote_start_waitup;
+ remote->stop_waitdown = etchremote_stop_waitdown;
+
+ /* transport methods */
+ remote->transport_control = etchremote_transport_control;
+ remote->transport_notify = etchremote_transport_notify;
+ remote->transport_query = etchremote_transport_query;
+ remote->set_session = etchremote_set_session;
+ remote->get_session = etchremote_get_session;
+
+ /* remote base */
+ remote->new_message = etchremote_new_message;
+ remote->send = etchremote_send;
+ remote->sendex = etchremote_sendex;
+ remote->begin_call = etchremote_begincall;
+ remote->end_call = etchremote_endcall;
+
+ return remote;
+}
+
+
+
diff --git a/binding-c/runtime/c/src/main/support/etch_serializer.c b/binding-c/runtime/c/src/main/support/etch_serializer.c
new file mode 100644
index 0000000..7f408ed
--- /dev/null
+++ b/binding-c/runtime/c/src/main/support/etch_serializer.c
@@ -0,0 +1,926 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_serializer.c
+ */
+
+#include "etch_serializer.h"
+#include "etch_default_value_factory.h"
+#include "etch_arrayval.h"
+#include "etch_encoding.h"
+#include "etch_nativearray.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_map.h"
+#include "etch_log.h"
+
+// etch_log vars
+const char* ETCH_SERIALIZER_CATEGORY = "etch_serializer";
+
+etch_object* etchserializer_defexportval (etch_serializer* thisx, etch_object* objval);
+etch_object* etchserializer_defimportval (etch_serializer* thisx, etch_object* structval);
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * constructors/destructors
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * destroy_etch_serializer()
+ * etch_serializer destructor
+ */
+int destroy_etch_serializer(void* data)
+{
+ etch_serializer* thisx = (etch_serializer*)data;
+
+ if (!is_etchobj_static_content(thisx)){
+ etch_object_destroy(thisx->impl);
+ thisx->impl = NULL;
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * new_etch_serializer()
+ * etch_serializer constructor
+ */
+etch_serializer* new_etch_serializer(const int objsize)
+{
+ etch_serializer* newobj = (etch_serializer*) new_object
+ (sizeof(etch_serializer), ETCHTYPEB_SERIALIZER, CLASSID_NONE);
+
+ ((etch_object*)newobj)->destroy = destroy_etch_serializer;
+ newobj->import_value = etchserializer_defimportval;
+ newobj->export_value = etchserializer_defexportval;
+
+ return newobj;
+}
+
+
+
+
+/**
+ * etch_serializer_init()
+ * generic static intitializer for import export helper serializers.
+ * caller retains ownership of *all* parameters to this method.
+ * @param obj_type type of the object being serialized
+ * @param keyname name of an existing etch_field which will key the import
+ * and export object in its serialization map.
+ * @param obj_class numeric etch object type and class id of the object
+ being serialized.
+ * @param c2tmap a vf's map of numeric class to object type associations.
+ * @param vtor the validator object used to vet objects of this type.
+ * @param new_szr a pointer to the constructor for serializers of the
+ * specified type.
+ */
+int etch_serializer_init(etch_type* obj_type, const wchar_t* keyname,
+ const unsigned obj_class, etch_hashtable* c2tmap, etch_validator* vtor,
+ etch_serializer_ctor new_szr)
+{
+ etch_serializer* newimpexhelper = NULL;
+ /* serializer instantiation is awkward, since we are installing serializers
+ * to singleton types, but the class to type map is not singleton.
+ * investigate, and if reasonable, move the c2t maintenance to a higher
+ * level, and invoke this code only once per type instantiation.
+ * unfortunately a type does not indicate its correpsonding class,
+ * i.e. we know a type is "foo", but the type does not know it maps to
+ * ETCHTYPE_FOO, CLASSID_FOO. so here, the serializers, in their ctors,
+ * determine that mapping, and pass that class here. ideally we can eliminate
+ * both the c2t and class parameters from the serializer initialization.
+ */
+
+ /* get reference to field key having specified name, not owned here */
+ etch_field* szr_key = etchtype_get_field_by_name (obj_type, keyname);
+ if (NULL == szr_key) return -1;
+
+ /* map this class identifier to associated type object */
+ class_to_type_map_put(c2tmap, obj_class, obj_type);
+
+ /* if the type's serializer is already instantiated, we don't want
+ * to do so again. this is the awkardness commented above. */
+ if (etchtype_get_component_type(obj_type)) return 0;
+
+ /* set component type for arrays of specified class */
+ etchtype_set_component_type(obj_type, obj_class);
+
+ /* instantiate the new import/export helper */
+ newimpexhelper = new_szr(obj_type, szr_key);
+
+ /* attach, and assign ownership of, the new helper object to the type */
+ etchtype_set_impexphelper(obj_type, newimpexhelper);
+
+ return NULL == vtor? 0: /* attach the supplied validator to the type */
+ etchtype_put_validator(obj_type, clone_field(szr_key), (etch_object*) vtor);
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * exception serializer
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchserializer_exception_export_value()
+ * export value for any exception
+ * @param objval an etch_exception object. caller owns it,
+ * and presumably will destroy it upon return from this method.
+ * @return the exported disposable structvalue object. caller must cast it.
+ */
+etch_object* etchserializer_exception_export_value (etch_serializer* thisx, etch_object* objval)
+{
+ int result = 0;
+ const int THISINITSIZE = 2;
+ etch_structvalue* structval = NULL;
+ etch_exception* excpobj = NULL;
+
+
+ if (!is_etch_exception(objval))
+ {
+ ETCH_LOG(ETCH_SERIALIZER_CATEGORY, ETCH_LOG_ERROR, "value is not exception\n");
+ return NULL;
+ }
+ excpobj = (etch_exception*) objval;
+
+ structval = new_structvalue((etch_type*) thisx->type, THISINITSIZE);
+ result = structvalue_put(structval, clone_field(thisx->field),((etch_object*)etch_exception_get_message(excpobj))->clone(((etch_object*)etch_exception_get_message(excpobj))));
+ return (etch_object*) structval;
+}
+
+
+/**
+ * etchserializer_exception_import_value()
+ * import value for any exception.
+ * @param objval an etch_structvalue of appropriate type for the exception.
+ * caller retains ownership of this object as with all imports.
+ * @return an opaque etch object containing the imported exception.
+ *
+ * caller owns and must destroy the returned object.
+ * use: etch_exception* x = get_exception(returnedobj);
+ * to point into and inspect the exception.
+ */
+etch_object* etchserializer_exception_import_value
+ (etch_serializer* thisx, etch_object* objval, excptype_t xtype)
+{
+ wchar_t* unistring = NULL;
+ etch_string* impstring = NULL;
+ etch_exception* exception = NULL;
+ int result;
+
+ etch_structvalue* structval = (etch_structvalue*) objval;
+ if (!is_etch_struct(structval)) return NULL;
+ if (!structvalue_is_type(structval, thisx->type)) return NULL;
+
+ /* fetch the exception string out of the struct. not owned here. */
+ impstring = (etch_string*) structvalue_get (structval, thisx->field);
+ if (!is_etch_string(impstring)) return NULL;
+
+ // TODO: pool
+ result = etch_encoding_transcode_to_wchar(&unistring, impstring->v.value, impstring->encoding, impstring->byte_count, NULL);
+ ETCH_ASSERT(result != -1);
+
+ /* create an exception object from the deserialized string. this object
+ * manages the exception memory according to flags supplied it. we set
+ * such a flag here, ETCHEXCP_FREETEXT, indicating that the exception
+ * owns the text buffer we give it. thus we no longer own unistring */
+ exception = new_etch_exception (xtype);
+
+ etch_exception_set_message(exception, new_stringw(unistring));
+ etch_free(unistring);
+ return (etch_object*) exception;
+}
+
+
+/**
+ * etchserializer_excp_import_value()
+ * virtual import value for runtime exception.
+ * @param objval an etch_structvalue of type etch runtime exception.
+ * caller retains ownership of this object as with all imports.
+ * @return an opaque etch object containing the imported exception.
+ *
+ * caller owns and must destroy the returned object.
+ * use: etch_exception* x = get_exception(returnedobj);
+ * to point into and inspect the exception.
+ */
+etch_object* etchserializer_excp_import_value(etch_serializer* thisx, etch_object* objval)
+{
+ return etchserializer_exception_import_value(thisx, objval, EXCPTYPE_BUILTIN);
+}
+
+
+
+/**
+ * new_exception_serializer()
+ * etch_serializer_excp constructor
+ * @param type - not owned
+ * @param field - not owned
+ */
+etch_serializer* new_exception_serializer(etch_type* type, etch_field* field)
+{
+ etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);
+
+ ((etch_object*)newobj)->class_id = CLASSID_SERIALIZER_EXCP;
+ newobj->type = type; /* not owned */
+ newobj->field = field; /* not owned */
+
+ newobj->export_value = etchserializer_exception_export_value;
+ newobj->import_value = etchserializer_excp_import_value;
+
+ return newobj;
+}
+
+
+/**
+ * etch_serializer_exception_init()
+ * static intitializer for runtime exception serializer.
+ * creates the impexp serializer and installs it to the supplied type.
+ * @param thistype type of the serializer (runtime exception)
+ */
+int etch_serializer_exception_init(etch_type* thistype, etch_hashtable* c2tmap)
+{
+ const wchar_t* keyname = str_msg; /* vf static constant */
+ const unsigned thisclass
+ = ETCHMAKECLASS(ETCHTYPEB_EXCEPTION, CLASSID_EXCEPTION);
+
+ return etch_serializer_init(thistype, keyname, thisclass, c2tmap,
+ etchvtor_string_get(0), new_exception_serializer);
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * runtime exception serializer
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchserializer_rtxcp_export_value()
+ * virtual export value for runtime exception
+ * @param objval a, etchexception blob, caller owns it and presumably will
+ * destroy it upon return from this method.
+ * @return the exported disposable structvalue object. caller must cast it.
+ */
+etch_object* etchserializer_rtxcp_export_value(etch_serializer* thisx, etch_object* objval)
+{
+ return etchserializer_exception_export_value(thisx, objval);
+}
+
+
+/**
+ * etchserializer_rtxcp_import_value()
+ * virtual import value for runtime exception.
+ * @param objval an etch_structvalue of type etch runtime exception.
+ * caller retains ownership of this object as with all imports.
+ * @return an opaque etch object containing the imported exception.
+ *
+ * caller owns and must destroy the returned object.
+ * use: etch_exception* x = get_exception(returnedobj);
+ * to point into and inspect the exception.
+ */
+etch_object* etchserializer_rtxcp_import_value(etch_serializer* thisx, etch_object* objval)
+{
+ return etchserializer_exception_import_value(thisx, objval, EXCPTYPE_BUILTIN);
+}
+
+
+/**
+ * new_runtime_exception_serializer()
+ * etch_serializer_rtxcp constructor
+ * @param type - not owned
+ * @param field - not owned
+ */
+etch_serializer* new_runtime_exception_serializer(etch_type* type, etch_field* field)
+{
+ etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);
+
+ ((etch_object*)newobj)->class_id = CLASSID_SERIALIZER_RTXCP;
+ newobj->type = type; /* not owned */
+ newobj->field = field; /* not owned */
+
+ newobj->export_value = etchserializer_rtxcp_export_value;
+ newobj->import_value = etchserializer_rtxcp_import_value;
+
+ return newobj;
+}
+
+
+/**
+ * etch_serializer_rtxcp_init()
+ * static intitializer for runtime exception serializer.
+ * creates the impexp serializer and installs it to the supplied type.
+ * @param thistype type of the serializer (runtime exception)
+ */
+int etch_serializer_rtxcp_init(etch_type* thistype, etch_hashtable* c2tmap)
+{
+ const wchar_t* keyname = str_msg; /* vf static constant */
+ const unsigned thisclass
+ = ETCHMAKECLASS(ETCHTYPEB_EXCEPTION, CLASSID_RUNTIME_EXCEPTION);
+
+ return etch_serializer_init(thistype, keyname, thisclass, c2tmap,
+ etchvtor_string_get(0), new_runtime_exception_serializer);
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * auth exception serializer
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchserializer_authxcp_export_value()
+ * virtual export value for auth exception
+ * @param objval a, etchexception blob, caller owns it and presumably will
+ * destroy it upon return from this method.
+ * @return the exported disposable structvalue object. caller must cast it.
+ */
+etch_object* etchserializer_authxcp_export_value(etch_serializer* thisx, etch_object* objval)
+{
+ return etchserializer_exception_export_value(thisx, objval);
+}
+
+
+/**
+ * etchserializer_authxcp_import_value()
+ * virtual import value for runtime exception.
+ * @param objval an etch_structvalue of type etch runtime exception.
+ * caller retains ownership of this object as with all imports.
+ * @return a nonspecific etch object containing the imported exception.
+ *
+ * caller owns and must destroy the returned object.
+ * use: etch_exception* x = get_exception(returnedobj);
+ * to point into and inspect the exception.
+ */
+etch_object* etchserializer_authxcp_import_value(etch_serializer* thisx, etch_object* objval)
+{
+ return etchserializer_exception_import_value(thisx, objval, EXCPTYPE_BUILTIN);
+}
+
+
+/**
+ * new_auth_exception_serializer()
+ * etch_serializer_auth exception constructor
+ * @param type - not owned
+ * @param field - not owned
+ */
+etch_serializer* new_auth_exception_serializer(etch_type* type, etch_field* field)
+{
+ etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);
+
+ ((etch_object*)newobj)->class_id = CLASSID_SERIALIZER_AUTHXCP;
+ newobj->type = type; /* not owned */
+ newobj->field = field; /* not owned */
+
+ newobj->export_value = etchserializer_authxcp_export_value;
+ newobj->import_value = etchserializer_authxcp_import_value;
+
+ return newobj;
+}
+
+
+/**
+ * etch_serializer_authxcp_init()
+ * static intitializer for auth exception serializer.
+ * creates the impexp serializer and installs it to the supplied type.
+ * @param thistype type of the serializer (runtime exception)
+ */
+int etch_serializer_authxcp_init(etch_type* thistype, etch_hashtable* c2tmap)
+{
+ const wchar_t* keyname = str_msg; /* vf static constant */
+ const unsigned thisclass
+ = ETCHMAKECLASS(ETCHTYPEB_EXCEPTION, CLASSID_AUTH_EXCEPTION);
+
+ return etch_serializer_init(thistype, keyname, thisclass, c2tmap,
+ etchvtor_string_get(0), new_auth_exception_serializer);
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * list serializer
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchserializer_list_export_value()
+ * virtual export value for list
+ * @param objval an etch_arraylist containing values to export to tdo.
+ * caller retains ownership of this object; this method however sets the readonly
+ * mark on this object, such that it will not destroy its content, which was
+ * assigned to the arraylist wrapped in the returned struct.
+ * the c binding internally models a list type as an etch_arraylist.
+ * @return the exported structvalue object, containing an etch_arraylist.
+ * since we can't pass around raw arrays as does the java binding in this case,
+ * this serializer chooses to use an arraylist as the export object type for lists.
+ * while it may seem convoluted that the exported from and exported to objects are
+ * the same object type (not the same object however), the reason is the vf export
+ * interface contract, i.e. caller relinquishes memory for the export from object,
+ * and acquires memory for the export to objects.
+ */
+etch_object* etchserializer_list_export_value(etch_serializer* thisx, etch_object* objval)
+{
+ const int THISINITSIZE = 2;
+ int result = 0;
+
+ etch_arraylist* listobj = NULL;
+ etch_nativearray* param = NULL;
+ etch_iterator* iterator = NULL;
+ etch_structvalue* outstruct = NULL;
+ etch_object* current = NULL;
+ int i = 0;
+
+ if (!is_etch_arraylist(objval)) return NULL;
+ listobj = (etch_arraylist*) objval;
+
+ param = new_etch_nativearray(CLASSID_ARRAY_OBJECT, sizeof(etch_object*),1,listobj->count,0,0);
+
+ param->content_obj_type = ETCHTYPEB_PRIMITIVE;
+ param->content_class_id = CLASSID_OBJECT;
+
+ //take ownership of elements
+ listobj->is_readonly = TRUE;
+ param->is_content_owned = TRUE;
+
+ iterator = new_iterator(listobj, &listobj->iterable);
+
+ while(iterator->has_next(iterator)){
+ current = iterator->current_value;
+
+ param->put1(param,¤t,i++);
+ iterator->next(iterator);
+ }
+ etch_object_destroy(iterator);
+
+ /* assign ownership of the new list to the returned struct */
+ outstruct = new_structvalue((etch_type*) thisx->type, THISINITSIZE);
+
+ result = structvalue_put(outstruct, clone_field(thisx->field), (etch_object*) param);
+ return (etch_object*) outstruct; /* caller owns this structvalue */
+}
+
+
+/**
+ * etchserializer_list_import_value()
+ * virtual import value for etch list.
+ * @param objval an etch_structvalue of type etch list.
+ * caller retains ownership of this object as with all imports.
+ * @return a *disposable* etch_arraylist containing the imported data objects.
+ *
+ * note that the c binding works a bit differently that does the java here.
+ * java tdi populates the structvalue with an object[]. in c the tdi inserts a
+ * synchronized etch_arraylist to the structvalue.
+ */
+etch_object* etchserializer_list_import_value(etch_serializer* thisx, etch_object* objval)
+{
+ etch_arrayvalue* inarray = NULL;
+ etch_arraylist* outarray = NULL;
+ etch_structvalue* instruct = (etch_structvalue*) objval;
+ etch_iterator iterator;
+ etch_object* current = NULL;
+
+ if (!is_etch_struct(instruct)) return NULL;
+ if (!structvalue_is_type(instruct, thisx->type)) return NULL;
+
+ /* fetch the arrayvalue from the struct. struct owns it. */
+ inarray = (etch_arrayvalue*) structvalue_get(instruct, thisx->field);
+ if (!is_etch_arrayvalue(inarray)) return NULL;
+
+ outarray = new_etch_arraylist(0,0);
+ arrayvalue_set_iterator(inarray, &iterator);
+ while(iterator.has_next(&iterator)){
+ current = iterator.current_value;
+ etch_arraylist_add(outarray,current);
+ iterator.next(&iterator);
+ }
+
+ /* value factory import_custom_value() will destroy() the import struct
+ * which will call destructors on its content. we mark the import list
+ * object such that its content will not be destroyed with it, since
+ * we just copied that content to the return list */
+ arrayvalue_set_static_content(inarray, TRUE);
+
+ return (etch_object*) outarray; /* caller owns the returned list */
+}
+
+
+/**
+ * new_list_serializer()
+ * etch_serializer_list constructor
+ * @param type - not owned
+ * @param field - not owned
+ */
+etch_serializer* new_list_serializer(etch_type* type, etch_field* field)
+{
+ etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);
+
+ ((etch_object*)newobj)->class_id = CLASSID_SERIALIZER_LIST;
+ newobj->type = type; /* not owned */
+ newobj->field = field; /* not owned */
+
+ newobj->export_value = etchserializer_list_export_value;
+ newobj->import_value = etchserializer_list_import_value;
+
+ return newobj;
+}
+
+
+/**
+ * etch_serializer_list_init()
+ * static intitializer for list serializer.
+ * creates the list impex serializer and installs it to the supplied type.
+ * @param thistype type of the serializer (list)
+ */
+int etch_serializer_list_init(etch_type* thistype, etch_hashtable* c2tmap)
+{
+ const wchar_t* key_name = str_values; /* vf static constant */
+ const unsigned thisclass
+ = ETCHMAKECLASS(ETCHTYPEB_ETCHLIST, CLASSID_ETCH_LIST);
+
+ return etch_serializer_init(thistype, key_name, thisclass, c2tmap,
+ etchvtor_object_get(1), new_list_serializer);
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * map serializer
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchserializer_map_export_value()
+ * virtual export value for etch map
+ * @param objval an etch_hashtable containing values to export. caller owns it,
+ * and presumably will destroy it on return from this method. this method marks
+ * this object such that its destructor will not destroy its content objects,
+ * ownership of which is transferred here to the returned arraylist.
+ * @return the exported structvalue object in which the lone entry is an
+ * etch_arraylist containing the map keys and values as consecutive entries.
+ * caller owns this returned structvalue.
+ */
+etch_object* etchserializer_map_export_value(etch_serializer* thisx, etch_object* objval)
+{
+ int result = 0, mapsize = 0, listsize = 0, is_expected_maptype = 0;
+ etch_iterator iterator;
+ const int THISINITSIZE = 2;
+ const int is_this_a_set = (thisx->type->id == builtins._mt__etch_set->id);
+ etch_hashtable* impmap = NULL;
+ etch_nativearray* explist = NULL;
+ etch_structvalue* expstruct = NULL;
+ int i = 0;
+ if (!is_etch_hashtable(objval)) return NULL;
+
+ impmap = (etch_hashtable*) objval;
+ mapsize = etchmap_count(impmap);
+ listsize = is_this_a_set? mapsize: mapsize * 2;
+
+
+ /* the underlying hashtable for an etch map must be explicitly marked
+ * as having object-key, object-value content, since the export list
+ * content is object-key, object-value, object-key, object value, etc. */
+ is_expected_maptype = is_this_a_set?
+ impmap->content_type == ETCHHASHTABLE_CONTENT_OBJECT_NONE:
+ impmap->content_type == ETCHHASHTABLE_CONTENT_OBJECT_OBJECT;
+ if (!is_expected_maptype) return NULL;
+
+ /* create native array for export, marked such that
+ * destructors will not be called on list content */
+ explist = new_etch_nativearray(CLASSID_ARRAY_OBJECT, sizeof(etch_object*),1,listsize,0,0);
+ explist->content_obj_type = ETCHTYPEB_PRIMITIVE;
+ explist->content_class_id = CLASSID_OBJECT;
+
+ set_iterator(&iterator, impmap, &impmap->iterable);
+
+ while(iterator.has_next(&iterator)) /* copy map data to array */
+ {
+ explist->put1(explist,&iterator.current_key,i++);
+ if (!is_this_a_set)
+ {
+ explist->put1(explist,&iterator.current_value,i++);
+ }
+ iterator.next(&iterator);
+ }
+
+ /* mark caller's map such that it will not destroy its content,
+ * ownership of which we just transferred to the export array */
+ impmap->is_readonly_keys = impmap->is_readonly_values = TRUE;
+ impmap->freehook = etch_noop_clear_handler;
+
+ expstruct = new_structvalue((etch_type*) thisx->type, THISINITSIZE);
+
+ /* here we assign ownership of the new arraylist to the struct */
+ result = structvalue_put(expstruct, clone_field(thisx->field), (etch_object*)explist);
+
+ return (etch_object*) expstruct; /* caller owns this structvalue */
+}
+
+
+/**
+ * etchserializer_map_import_value()
+ * virtual import value for etch map.
+ * @param objval an etch_structvalue of type etch map, wrapping a
+ * single entry of etch_arrayvalue, wrapping an etch_arraylist in which
+ * consecutive entries represent a map key and a map value. this method
+ * will mark the arraylist such that it will not destroy its object content.
+ * caller retains ownership of this object as with all imports.
+ * @return a disposable etch_hashtable containing the imported data objects.
+ * caller must cast it and destroy it.
+ *
+ * note that the c binding works a bit differently that does the java here.
+ * java tdi populates the structvalue with an object[]. in c the tdi inserts a
+ * synchronized etch_arraylist wrapped by an etch_arrayvalue, to the structvalue.
+ */
+etch_object* etchserializer_map_import_value(etch_serializer* thisx, etch_object* objval)
+{
+ int i = 0, result = 0, listcount = 0;
+ etch_hashtable* expmap = NULL;
+ etch_arrayvalue* implist = NULL;
+ etch_structvalue* impstruct = NULL;
+ const int is_this_a_set = thisx->type->id == builtins._mt__etch_set->id;
+ if (!is_etch_struct(objval)) return NULL;
+
+ impstruct = (etch_structvalue*) objval;
+ if (!structvalue_is_type(impstruct, thisx->type)) return NULL;
+
+ /* get the synchronized arraylist out of the struct and mark the list
+ * such that it no longer owns its object content, ownership of which we
+ * are transferring to the exported map.
+ */
+ implist = (etch_arrayvalue*) structvalue_get(impstruct, thisx->field);
+ if (!is_etch_arrayvalue(implist)) return NULL;
+ arrayvalue_set_static_content(implist, TRUE);
+ listcount = implist->list->count;
+
+ /* instantiate a map configured appropriately for the import data */
+ /* todo code a new_synchronized_map() constructor and use it here */
+ if(is_this_a_set) {
+ expmap = new_etch_set(listcount);
+ }
+ else {
+ expmap = new_etch_map(listcount / 2);
+ }
+ expmap->content_obj_type = implist->content_obj_type;
+ expmap->content_class_id = implist->content_class_id;
+
+ while(i < (const int) listcount) /* copy data objects to the map */
+ {
+ etch_object* key = arrayvalue_get(implist, i++);
+ if (NULL == key) continue;
+
+ if (is_this_a_set)
+ result = etchmap_set_add(expmap, key);
+ else
+ result = etchmap_map_add(expmap, key, arrayvalue_get(implist, i++));
+
+ if (-1 == result)
+ ETCH_LOG(ETCH_SERIALIZER_CATEGORY, ETCH_LOG_ERROR, "map insert error on key %u", key);
+ }
+ return (etch_object*) expmap;
+}
+
+/**
+ * new_map_serializer()
+ * etch_serializer_map constructor
+ * @param type - not owned
+ * @param field - not owned
+ */
+etch_serializer* new_map_serializer(etch_type* type, etch_field* field)
+{
+ etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);
+
+ ((etch_object*)newobj)->class_id = CLASSID_SERIALIZER_MAP;
+ newobj->type = type; /* not owned */
+ newobj->field = field; /* not owned */
+
+ newobj->export_value = etchserializer_map_export_value;
+ newobj->import_value = etchserializer_map_import_value;
+
+ return newobj;
+}
+
+
+/**
+ * etch_serializer_map_init()
+ * static intitializer for map serializer.
+ * creates the map serializer and installs it to the supplied type.
+ * @param thistype type of the serializer (map)
+ */
+int etch_serializer_map_init(etch_type* thistype, etch_hashtable* c2tmap)
+{
+ const wchar_t* keyname = str_keys_values; /* vf static constant */
+ const unsigned thisclass
+ = ETCHMAKECLASS(ETCHTYPEB_ETCHMAP, CLASSID_ETCH_MAP);
+
+ return etch_serializer_init(thistype, keyname, thisclass, c2tmap,
+ etchvtor_object_get(1), new_map_serializer);
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * set serializer
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchserializer_set_export_value()
+ * virtual export value for etch set
+ * @param objval an etch_hashtable containing values to export. caller owns it,
+ * and presumably will destroy it on return from this method. this method marks
+ * the map object such that it will not destroy its content objects.
+ * @return the exported structvalue object in which the lone entry is an
+ * etch_arrayvalue containing the map keys
+ * caller owns memory for the returned object.
+ */
+etch_object* etchserializer_set_export_value(etch_serializer* thisx, etch_object* objval)
+{
+ return etchserializer_map_export_value(thisx, objval); /* caller owns */
+}
+
+
+/**
+ * etchserializer_set_import_value()
+ * virtual import value for etch set.
+ * @param objval an etch_structvalue of type etch set, wrapping a single entry
+ * of etch_arrayvalue, wrapping an etch_arraylist containing the set values.
+ * this method will mark the arraylist such that it will not destroy its content.
+ * caller retains ownership of this object as with all imports.
+ * @return a disposable etch_hashtable containing the imported data objects.
+ * caller must cast it and destroy it.
+ * note that the c binding works a bit differently that does the java here.
+ * java tdi populates the structvalue with an object[]. in c the tdi inserts a
+ * synchronized etch_arraylist wrapped by an etch_arrayvalue, to the structvalue.
+ */
+etch_object* etchserializer_set_import_value(etch_serializer* thisx, etch_object* objval)
+{
+ return etchserializer_map_import_value(thisx, objval);
+}
+
+
+/**
+ * new_set_serializer()
+ * etch_serializer_set constructor
+ * @param type - not owned
+ * @param field - not owned
+ */
+etch_serializer* new_set_serializer(etch_type* type, etch_field* field)
+{
+ etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);
+
+ ((etch_object*)newobj)->class_id = CLASSID_SERIALIZER_SET;
+ newobj->type = type; /* not owned */
+ newobj->field = field; /* not owned */
+
+ newobj->export_value = etchserializer_set_export_value;
+ newobj->import_value = etchserializer_set_import_value;
+
+ return newobj;
+}
+
+
+/**
+ * etch_serializer_set_init()
+ * static intitializer for set serializer.
+ * creates the set serializer and installs it to the supplied type.
+ * @param thistype type of the serializer (set)
+ */
+int etch_serializer_set_init(etch_type* thistype, etch_hashtable* c2tmap)
+{
+ const wchar_t* keyname = str_keys; /* vf static constant */
+ const unsigned thisclass
+ = ETCHMAKECLASS(ETCHTYPEB_HASHTABLE, CLASSID_ETCH_SET);
+
+ return etch_serializer_init(thistype, keyname, thisclass, c2tmap,
+ etchvtor_object_get(1), new_set_serializer);
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * date serializer
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchserializer_date_export_value()
+ * virtual export value for date
+ * @param objval an etch_date object for export to tdo.
+ * caller retains ownership of this object.
+ * @return the exported structvalue object. caller owns it and must cast it.
+ */
+etch_object* etchserializer_date_export_value(etch_serializer* thisx, etch_object* objval)
+{
+ const int THISINITSIZE = 2;
+ etch_structvalue* expstruct = NULL;
+ if (!is_etch_date(objval)) return NULL;
+
+ expstruct = new_structvalue((etch_type*) thisx->type, THISINITSIZE);
+#ifdef WIN32
+ // assign ownership of the wrapped 64-bit integer to the struct
+ // on win32 systems the time_t struct is a int64
+ structvalue_put(expstruct, clone_field(thisx->field),
+ (etch_object*) new_int64(((etch_date*)objval)->value));
+#else
+ // assign ownership of the wrapped 32-bit integer to the struct
+ // on linux systems the time_t struct is a int32, to get no negativ
+ // value here, we cast the 32-bit value to unsigned long int
+ structvalue_put(expstruct, clone_field(thisx->field),
+ (etch_object*) new_int64((unsigned long int)((etch_date*)objval)->value));
+#endif
+ return (etch_object*) expstruct; /* caller owns this structvalue */
+}
+
+
+/**
+ * etchserializer_date_import_value()
+ * virtual import value for etch date.
+ * @param objval an etch_structvalue of type etch date.
+ * caller retains ownership of this object as with all imports.
+ * @return a *disposable* etch_date object
+ */
+etch_object* etchserializer_date_import_value(etch_serializer* thisx, etch_object* objval)
+{
+ etch_date* outdate = NULL;
+ etch_int64* timeval = NULL;
+ etch_structvalue* instruct = NULL;
+ if (!is_etch_struct(objval)) return NULL;
+
+ instruct = (etch_structvalue*) objval;
+ if (!structvalue_is_type(instruct, thisx->type)) return NULL;
+
+ /* fetch the arrayvalue out of the struct. struct owns it */
+ timeval = (etch_int64*) structvalue_get(instruct, thisx->field);
+ if (!is_etch_int64(timeval)) return NULL;
+
+ outdate = new_date();
+ outdate->value = (time_t) timeval->value;
+
+ return (etch_object*) outdate; /* caller owns this object */
+}
+
+
+/**
+ * new_date_serializer()
+ * etch_serializer_date constructor
+ * @param type - not owned
+ * @param field - not owned
+ */
+etch_serializer* new_date_serializer(etch_type* type, etch_field* field)
+{
+ etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);
+
+ ((etch_object*)newobj)->class_id = CLASSID_SERIALIZER_DATE;
+ newobj->type = type; /* not owned */
+ newobj->field = field; /* not owned */
+
+ newobj->export_value = etchserializer_date_export_value;
+ newobj->import_value = etchserializer_date_import_value;
+
+ return newobj;
+}
+
+
+/**
+ * etch_serializer_date_init()
+ * static intitializer for date serializer.
+ * creates the date serializer and installs it to the supplied type.
+ * @param thistype type of the serializer (date)
+ */
+int etch_serializer_date_init(etch_type* thistype, etch_hashtable* c2tmap)
+{
+ const wchar_t* keyname = str_date_time; /* vf static constant */
+ const unsigned thisclass
+ = ETCHMAKECLASS(ETCHTYPEB_PRIMITIVE, CLASSID_DATE);
+
+ return etch_serializer_init(thistype, keyname, thisclass, c2tmap,
+ etchvtor_int64_get(0), new_date_serializer);
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * misc
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+ etch_object* etchserializer_defexportval(etch_serializer* s, etch_object* x)
+{
+ return NULL;
+}
+
+etch_object* etchserializer_defimportval (etch_serializer* s, etch_object* x)
+{
+ return NULL;
+}
+
diff --git a/binding-c/runtime/c/src/main/support/etch_sessionint.c b/binding-c/runtime/c/src/main/support/etch_sessionint.c
new file mode 100644
index 0000000..3eb4b82
--- /dev/null
+++ b/binding-c/runtime/c/src/main/support/etch_sessionint.c
@@ -0,0 +1,231 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sessionint.c
+ * session interface
+ */
+
+#include "etch_sessionint.h"
+#include "etch_default_value_factory.h"
+#include "etch_message.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+/*
+static const char* LOG_CATEGORY = "etch_session";
+*/
+
+int etchsession_def_session_control (void*, etch_event*, etch_object*);
+int etchsession_def_session_notify (void*, etch_event*);
+etch_object* etchsession_def_session_query (void*, etch_query*);
+
+char* ETCHDSIF = "DSIF";
+
+
+/**
+ * new_default_sessson_interface
+ * return a session interface populated with defaults for virtuals.
+ * caller owns returned object, not an etch object, use etch_free() to destroy.
+ */
+i_session* new_default_session_interface(void* thisx)
+{
+ i_session* newi = etch_malloc(sizeof(i_session), ETCHTYPEB_RAWOBJECT);
+ newi->session_control = etchsession_def_session_control;
+ newi->session_notify = etchsession_def_session_notify;
+ newi->session_query = etchsession_def_session_query;
+ newi->thisx = thisx;
+ return newi;
+}
+
+
+/**
+ * new_session_interface
+ * return a session interface populated with specified virtuals.
+ * caller owns returned object, not an etch object, use etch_free() to destroy.
+ */
+i_session* new_session_interface(void* thisx,
+ etch_session_control sc, etch_session_notify sn, etch_session_query sq)
+{
+ i_session* newi = new_default_session_interface(thisx);
+ if (sc) newi->session_control = sc;
+ if (sn) newi->session_notify = sn;
+ if (sq) newi->session_query = sq;
+ return newi;
+}
+
+
+/**
+ * clone_session()
+ */
+i_session* clone_session(void* thisx, const i_session* thatsession)
+{
+ i_session* newsession = thatsession? new_default_session_interface(thisx): NULL;
+
+ if (newsession)
+ memcpy(newsession, thatsession, sizeof(i_session));
+
+ return newsession;
+}
+
+
+/**
+ * new_default_objsessson_interface
+ * return an objsession interface populated with defaults for virtuals.
+ * caller owns returned object, not an etch object, use etch_free() to destroy.
+ */
+i_objsession* new_default_objsession_interface (void* thisx)
+{
+ i_objsession* newi = etch_malloc(sizeof(i_objsession), ETCHTYPEB_RAWOBJECT);
+ newi->_session_control = etchsession_def_session_control;
+ newi->_session_notify = etchsession_def_session_notify;
+ newi->_session_query = etchsession_def_session_query;
+ newi->thisx = thisx;
+ return newi;
+}
+
+
+/**
+ * new_objsession_interface
+ * return an objsession interface populated with specified virtuals.
+ * caller owns returned object, not an etch object, use etch_free() to destroy.
+ */
+i_objsession* new_objsession_interface (void* thisx,
+ etch_session_control sc, etch_session_notify sn, etch_session_query sq)
+{
+ return (i_objsession*) new_session_interface(thisx, sc, sn, sq);
+}
+
+
+/**
+ * etchsession_get_objinfo()
+ * extract object info from the object passed to session methods.
+ */
+void etchsession_get_objinfo (etch_objsession_objinfo* p, void* evt)
+{
+ memset(p, 0, sizeof(etch_objsession_objinfo));
+ if (NULL == evt) return;
+ p->obj = (etch_object*) evt;
+ ((etch_object*)p)->obj_type = ((etch_object*) evt)->obj_type;
+ ((etch_object*)p)->class_id = ((etch_object*) evt)->class_id;
+
+ if (is_etch_unwantedmsg (p->obj))
+ { p->msg = (etch_object*) ((etch_unwanted_message*) p->obj)->message;
+ p->whofrom = ((etch_unwanted_message*) p->obj)->whofrom;
+ }
+ else
+ if (is_etch_message (p->obj))
+ p->msg = p->obj;
+
+ if (is_etch_message (p->msg))
+ {
+ p->is_message = TRUE;
+ p->msg_aname = message_aname ((etch_message*) p->msg);
+
+ if (is_etch_exception(p->msg))
+ { p->is_exception = TRUE;
+ p->exception = ((etch_exception*) p->msg);
+ }
+ else
+ { p->resobj = message_get ((etch_message*) p->msg, builtins._mf_result);
+ if (is_etch_exception(p->resobj))
+ { p->is_exception = TRUE;
+ p->exception = (etch_exception*) p->resobj;
+ }
+ }
+ }
+}
+
+
+/**
+ * etchsession_destroy_objparam()
+ * identify, log and destroy the specified session parameter object.
+ */
+void etchsession_destroy_objparam (void* evt, char* caller)
+{
+ char *msgmask = 0, *descrip = 0, *SESSION_ = "session_";
+ etch_objsession_objinfo objinfo;
+ if (NULL == evt) return;
+
+ etchsession_get_objinfo (&objinfo, evt);
+
+ if (objinfo.is_message)
+ {
+ if (objinfo.is_exception)
+ { msgmask = "%s%s disposing '%s'\n";
+ descrip = objinfo.exception? etch_exception_get_message((etch_exception*) objinfo.exception)->v.valc: 0;
+ }
+ else
+ { msgmask = "%s%s disposing message '%s'\n";
+ descrip = objinfo.msg_aname;
+ }
+
+ if (!descrip) descrip = "?";
+ ETCH_LOG(ETCHDSIF, ETCH_LOG_DEBUG, msgmask, SESSION_, caller, descrip);
+ }
+ else
+ { msgmask = "%s%s disposing type %x class %x\n";
+ ETCH_LOG(ETCHDSIF, ETCH_LOG_XDEBUG, msgmask, SESSION_, caller,
+ objinfo.obj_type, objinfo.class_id);
+ }
+
+ etch_object_destroy(objinfo.obj);
+ objinfo.obj = NULL;
+
+}
+
+
+/**
+ * etchsession_def_session_control()
+ * @param obj caller this.
+ * @param evt some etch object or null.
+ * @param v some etch object or null.
+ */
+int etchsession_def_session_control (void* obj, etch_event* evt, etch_object* v)
+{
+ etchsession_destroy_objparam (evt, "control");
+ // ETCHOBJ_DESTROY();
+ if(((etch_object*)v))
+ ((etch_object*)v)->destroy(((etch_object*)v));
+ v = NULL;
+ return 0;
+}
+
+
+/**
+ * etchsession_def_session_notify()
+ * @param obj caller this.
+ * @param evt some etch object or null.
+ */
+int etchsession_def_session_notify (void* obj, etch_event* evt)
+{
+ etchsession_destroy_objparam (evt, "notify");
+ return 0;
+}
+
+
+/**
+ * etchsession_def_session_query()
+ * @param obj caller this.
+ * @param query some etch object or null.
+ */
+etch_object* etchsession_def_session_query (void* obj, etch_query* query)
+{
+ etchsession_destroy_objparam (obj, "query");
+ return NULL;
+}
diff --git a/binding-c/runtime/c/src/main/support/etch_simpletimer.c b/binding-c/runtime/c/src/main/support/etch_simpletimer.c
new file mode 100644
index 0000000..e2e8b07
--- /dev/null
+++ b/binding-c/runtime/c/src/main/support/etch_simpletimer.c
@@ -0,0 +1,186 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_simpletimer.c
+ * thread-per-use one-shot relative timer
+ * this should be replaced with a timer pool implementation when development resources permit
+ */
+
+#include "etch_simpletimer.h"
+#include "etch_mutex.h"
+#include "etch_exception.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+typedef struct etch_timerdata
+{
+ void* userdata;
+ int durationms;
+ int is_started;
+ etch_timer_callback usercallback;
+
+} etch_timerdata;
+
+void etch_timerproc(void* data);
+etch_timerdata* new_timerdata(const int, void*, etch_timer_callback);
+
+
+
+/**
+ * new_timer()
+ * etch_timer constructor
+ * @param durationms timer duration in milliseconds.
+ * @param passthrudata user data to be returned in expiration callback.
+ * caller retains ownership of this memory.
+ * @param callback expiration callback void (*callback) (void*, int);
+ * in which creator's passthrudata is passed as the first parameter, and the timeout
+ * reason passed in parameter 2 (reason value 0 is signaled, 1 is timeout, -1 error).
+ * @return the timer object. caller owns this object but must not destroy it until
+ * after the expiration callback has completed and returned.
+ */
+etch_timer* new_timer(const int durationms, void* passthrudata, etch_timer_callback callback)
+{
+ etch_thread* timer_thread = NULL;
+ etch_timerdata* timerdata = NULL;
+ if (NULL == callback || durationms <= 0) return NULL;
+ timerdata = new_timerdata(durationms, passthrudata, callback);
+
+ /* create thread in wait state */
+ if (NULL == (timer_thread = new_thread(etch_timerproc, timerdata)))
+ etch_free(timerdata);
+ else /* timerdata wrapper freed in etch_timerproc */
+ { timer_thread->params.is_own_data = FALSE;
+ /* semikludge to complete thread start and thus avoid crash in the
+ * event timer is destroyed without having been started.
+ * surely there is a cleaner way to do this. */
+ etch_sleep(1);
+ }
+
+ return timer_thread;
+}
+
+
+/**
+ * etch_timer_start()
+ * start the specified timer. a timer must always be explicitly started.
+ */
+int etch_timer_start(etch_timer* timer_thread)
+{
+ int result = -1;
+ etch_thread_params* params = timer_thread? &timer_thread->params: NULL;
+ etch_timerdata* timerdata = params? (etch_timerdata*) params->data: NULL;
+
+ if (timerdata)
+ if (0 == (result = timer_thread->start(timer_thread)))
+ timerdata->is_started = TRUE;
+
+ return result;
+}
+
+
+/**
+ * etch_timer_stop()
+ * signal specified timer to stop waiting and exit its thread.
+ */
+int etch_timer_stop(etch_timer* timer_thread)
+{
+ int result = -1;
+ etch_wait_t* waiter;
+ if (NULL == timer_thread) return -1;
+ waiter = timer_thread->params.waitobj;
+
+ if (waiter && timer_thread->params.threadstate == ETCH_THREADSTATE_STARTED)
+ {
+ etch_wait_set(waiter, 1);
+ etch_join(timer_thread); //wait until timer thread finished
+ }
+
+ return result;
+}
+
+
+/* - - - - - - -
+ * private
+ * - - - - - - -
+ */
+
+/**
+ * new_timerdata()
+ * create timer thread data
+ */
+struct etch_timerdata* new_timerdata(const int durationms, void* passthrudata,
+ etch_timer_callback callback)
+{
+ etch_timerdata* timerdata = etch_malloc(sizeof(etch_timerdata), 0);
+ timerdata->userdata = passthrudata;
+ timerdata->durationms = durationms;
+ timerdata->usercallback = callback;
+ timerdata->is_started = FALSE;
+ return timerdata;
+}
+
+
+/**
+ * etch_timerproc()
+ * timer thread procedure
+ */
+void etch_timerproc(void* data)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_thread_params* params = (etch_thread_params*) data;
+ etch_timerdata* timerdata = (etch_timerdata*) params->data;
+ etch_timer_callback usercallback = timerdata->usercallback;
+ etch_thread* threadx = params->etchobj;
+ const int durationms = timerdata->durationms;
+ void* userdata = timerdata->userdata;
+ int result = 0;
+ ETCH_ASSERT(usercallback);
+
+ if (NULL == threadx->params.waitobj) {
+ // TODO: pool
+ status = etch_wait_create(&threadx->params.waitobj, NULL);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+ /* start timer and wait for expiration or signal */
+ status = etch_wait_timedwait(threadx->params.waitobj, 1, durationms);
+ switch(status) {
+ // TODO refactor this stuff and remove ETCH_TIMEOUT ..
+ case ETCH_SUCCESS:
+ result = ETCH_INTERRUPTED;
+ break;
+ case ETCH_ETIMEOUT:
+ result = ETCH_TIMEOUT;
+ break;
+ default:
+ result = -1;
+ }
+
+ //ETCHOBJ_FREE(); /* etch_timerdata wrapper */
+ if (params->data)
+ etch_free(params->data);
+ params->data = NULL;
+
+
+ etch_wait_destroy(threadx->params.waitobj);
+ threadx->params.waitobj = NULL;
+
+ /* pass ownership of userdata back to user */
+ usercallback(userdata, result);
+}
+
diff --git a/binding-c/runtime/c/src/main/support/etch_sourceint.c b/binding-c/runtime/c/src/main/support/etch_sourceint.c
new file mode 100644
index 0000000..3d0a2f6
--- /dev/null
+++ b/binding-c/runtime/c/src/main/support/etch_sourceint.c
@@ -0,0 +1,101 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sourceint.c
+ * source interface
+ */
+
+#include "etch_sourceint.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+
+/*
+static const char* LOG_CATEGORY = "etch_source_intf";
+*/
+
+void* etch_source_def_get_handler (void* thisx);
+void etch_source_def_set_handler (void* thisx, void* handler);
+int destroy_i_source(void*);
+
+
+/**
+ * new_default_source_interface
+ * return a source interface populated with defaults for virtuals.
+ * this interface *is* an etch object, use destroy()
+ */
+i_source* new_default_source_interface()
+{
+ i_source* newi = (i_source*) new_object(sizeof(i_source), ETCHTYPEB_SOURCE, CLASSID_SOURCE);
+
+ ((etch_object*)newi)->destroy = destroy_i_source;
+ newi->get_handler = etch_source_def_get_handler;
+ newi->set_handler = etch_source_def_set_handler;
+ newi->itransport = new_default_transport_interface();
+ return newi;
+}
+
+
+/**
+ * new_source_interface
+ * return a source interface populated with specified virtuals.
+ * this interface object *is* an etch object, use destroy()
+ */
+i_source* new_source_interface(void* thisx,
+ etch_source_get_handler gh, etch_source_set_handler sh, i_transport* xp)
+{
+ i_source* newi = (i_source*) new_object(sizeof(i_source),
+ ETCHTYPEB_SOURCE, CLASSID_SOURCE);
+
+ ((etch_object*)newi)->destroy = destroy_i_source;
+ newi->get_handler = gh? gh: etch_source_def_get_handler;
+ newi->set_handler = sh? sh: etch_source_def_set_handler;
+ newi->itransport = xp? xp: new_default_transport_interface();
+ newi->thisx = thisx;
+ return newi;
+}
+
+
+/**
+ * destroy_i_source
+ * i_source destructor
+ */
+int destroy_i_source(void* data)
+{
+ i_source* thisx = (i_source*)data;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ { etch_free(thisx->itransport);
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
+
+
+void* etch_source_def_get_handler (void* thisx)
+{
+ return NULL;
+}
+
+
+void etch_source_def_set_handler (void* thisx, void* handler)
+{
+
+}
+
diff --git a/binding-c/runtime/c/src/main/support/etch_stub.c b/binding-c/runtime/c/src/main/support/etch_stub.c
new file mode 100644
index 0000000..a78892f
--- /dev/null
+++ b/binding-c/runtime/c/src/main/support/etch_stub.c
@@ -0,0 +1,877 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_stub.c
+ * contains the base runtime code for either type of stub.
+ *
+ * the generated stub_xxxx_server will define the thread procedures for each message
+ * type in the service. for each such type, it will set the virtual stub helpers in
+ * the type objects, to point to these thread functions. each such thread procedure
+ * will call the user-coded calculation in the impl_xxxx_server for that message
+ * type; for example, for an "add" message, it will get the fields from the service
+ * value factory which are the arguments to the calculation, call the calculation in
+ * the server impl, insert the result of the calculation into the reply message, and
+ * call the delivery service transport_message to send the reply back to the sender.
+ */
+
+#include "etch.h"
+#include "etch_svcobj_masks.h"
+#include "etch_stub.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+#include "etch_object.h"
+#include "etch_message.h"
+static const char* LOG_CATEGORY = "etch_stub";
+
+int etchstub_session_notify_ex (void*, void*, etch_event*);
+int etchstub_session_message(void*, etch_who*, etch_message*);
+int etchstub_session_control(void*, etch_event*, etch_object*);
+int etchstub_session_notify (void*, etch_event*);
+etch_object* etchstub_session_query (void*, etch_query*);
+i_objsession* etchstub_get_session_callbacks_from (void* _obj);
+
+char* scstr = "session control", *snstr = "session notify", *sqstr = "session query";
+char* logmask1 = "%s not routed to client\n", *logmask2 = "%s not registered\n";
+
+
+
+#if(0)
+____________________________________________________________________________________
+the inheritance hierarchy becomes indefinite at this point. a stub should be able
+to query a service object for a particular interface, down through its inheritance
+chain. for example, we need to find who has implemented obj_session and call methods
+on it. a use case is a custom transport implemented as a dynamically loaded dll.
+the stub needs therefore to be able to call into objects it has never seen before.
+
+for the present we will make some assumptions as to the inheritance hierarchy, that
+being that given a service interface, we know its inheritance hierarchy. following
+is a diagram illustrating the inheritance of a service interface named XXXX.
+all arrows direct down unless indicated by an arrowhead. (e) indicates extends,
+(i) indicates implements. this diagram is upside-down to the way we normally see
+the hierarchy.
+ (obj)
+ |
+ XXXX <- - - - - - - - - - (stub)
+ | |
+ (e) --------- ---------- (e)
+ | |
+ XXXX_server remote_XXXX
+ | \ (i) | (e)
+ (i) | ------> remote_XXXX_server
+ obj_session <-- base_XXXX_server
+ (e) |
+ impl_XXXX_server
+____________________________________________________________________________________
+#endif
+
+
+#if(0)
+
+IMPL_XXXX_SERVER
+ - ctor(REMOTE_XXXX_CLIENT)
+ - usermethod_1_impl(); ...
+ - usermethod_n_impl();
+
+ BASE_XXXX_SERVER
+ - usermethod_1_stub(); ...
+ - usermethod_n_stub();
+ OBJSESSION
+ - _session_control; _session_notify; _session_query; (stubs)
+ XXXX_SERVER
+ - any server-directed methods go here
+ XXXX
+ - see user method impls above
+
+
+STUBXXXX<T extends XXXX> extends STUBBASE<T>
+ - ctor(DeliveryService, pool, pool);
+ - stubhelper();
+
+ STUBBASE<T> (DeliveryService, pool, pool)
+ T obj;
+ DeliveryService, Pool, Pool;
+ stub_helper();
+
+ STUBPOOLRUNNABLE
+ - set(stub_helper, who, msg); (set state, not in java)
+ - stubhelper(stub, delsvc, obj, who, msg);
+
+ SESSIONMESSAGE
+ - session_message(who, message);
+ { stubhelper = message.type.get_helper()
+ stubhelper(delsvc, obj, who, msg); // the stubhelper impl calls threadpool run()
+ }
+ - session_control; session_notify; session_query;
+
+
+STUBXXXSERVER (generated, hand-codable)
+ - ctor(DeliveryService, XXXXSERVER, pool, pool);
+ - for each type in the vf,
+ -- implement a run method (threadproc) for the message type
+ -- implement a stub helper method which calls the threadpool run using the threadproc above
+
+ STUBXXXX
+
+#endif
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * stub base method implementations
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * destroy_stub()
+ * etch_stub destructor. destroy stub base object.
+ * the stub base does not destroy its xxxx_either_stub wrapper.
+ */
+int destroy_stub(void* data)
+{
+ etch_stub* thisx = (etch_stub*)data;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ /* note that a delivery service session is the stub's session.
+ * the delivery service owns the stub, and the stub owns this shared
+ * session. the stub wrapper is the session interface's thisx.
+ * the delivery service gets xxxx_either_stub* stubbobj from its
+ * isessionmsg->thisx, and etch_stub* stubbase from stubobj.stub_base.
+ * destroying the stub will destroy the shared i_sessionmessage, so
+ * the delivery service's session will become invalid once its stub
+ * is destroyed. see transport.destroy_delivery_service_stub().
+ */
+ etch_object_destroy(thisx->isessionmsg);
+
+ if (thisx->obj && thisx->is_implobj_owned)
+ etch_object_destroy(thisx->obj);
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * new_stub()
+ * etch_stub (stub base) constructor.
+ */
+etch_stub* new_stub (void* implobj, unsigned char stubtype,
+ i_delivery_service* ids, etch_threadpool* qp, etch_threadpool* fp)
+{
+ i_sessionmessage* ism = NULL;
+
+ etch_stub* stubbase = (etch_stub*) new_object
+ (sizeof(etch_stub), ETCHTYPEB_STUB, CLASSID_STUB);
+
+ ETCH_ASSERT(implobj && stubtype && qp && fp);
+ ETCH_ASSERT(is_etch_ideliverysvc(ids));
+
+ stubbase->stub_type = stubtype;
+ ((etch_object*)stubbase)->destroy = destroy_stub;
+
+ stubbase->obj = implobj; /* server or client impl e.g. i_xxxx_server* */
+ stubbase->delivery_service = ids;
+
+ /* instantiate i_sessionmessage session interface. note that the interface's
+ * thisx pointer is this base stub object, and that the base stub's container
+ * xxxx_either_stub* is the base stub.stubobj.
+ */
+ ism = new_sessionmsg_interface (stubbase, etchstub_session_message, NULL);
+ stubbase->isessionmsg = ism;
+ stubbase->session_message = ism->session_message = etchstub_session_message;
+ stubbase->session_control = ism->session_control = etchstub_session_control;
+ stubbase->session_notify = ism->session_notify = etchstub_session_notify;
+ stubbase->session_query = ism->session_query = etchstub_session_query;
+
+ /* set delivery service session interface to be the stub's i_sessionmessage.
+ * in a c binding translation from java binding, a reference to a stub object
+ * is only via via the delivery service's session_message interface, shared
+ * between the stub and the delivery service.
+ * the delivery service must have provided i_transportmessage implementations,
+ * in particular for set_session(). the instantiator of the delivery service
+ * must therefore override the delivery service's i_transportmessage.
+
+ * fyi: ids->itm->thisx is mailbox manager.
+ * ids->itm is mboxmgr's transport interface, which is the delivery service.
+ * set session of next lower level (delivery service) to the stub's session.
+ */
+ ids->itm->set_session (ids, stubbase->isessionmsg);
+
+ /* copy impl's i_objsession to the stub for convenience */
+ stubbase->impl_callbacks = etchstub_get_session_callbacks_from (implobj);
+
+ return stubbase;
+}
+
+
+/**
+ * etchstub_get_session_callbacks_from()
+ * extract objsession interface from specified stub implementor object.
+ */
+i_objsession* etchstub_get_session_callbacks_from (void* _obj)
+{
+ i_objsession* iobjsession = NULL;
+ etch_object* obj = (etch_object*) _obj;
+ const int this_objtype = obj? ((etch_object*)obj)->obj_type: 0;
+
+ switch(this_objtype)
+ {
+ case ETCHTYPEB_EXESERVERIMPL:
+ { xxxx_server_impl* server_impl = (xxxx_server_impl*) obj;
+ iobjsession = server_impl->iobjsession;
+ break;
+ }
+
+ case ETCHTYPEB_EXECLIENTIMPL:
+ { xxxx_client_impl* client_impl = (xxxx_client_impl*) obj;
+ iobjsession = client_impl->iobjsession;
+ break;
+ }
+
+ case ETCHTYPEB_EXESERVERBASE:
+ { i_xxxx_server* iserver = (i_xxxx_server*) obj;
+ xxxx_server_impl* server_impl = (xxxx_server_impl*) iserver->thisx;
+ ETCH_ASSERT(server_impl && ((etch_object*)server_impl)->obj_type == ETCHTYPEB_EXESERVERIMPL);
+ iobjsession = server_impl->iobjsession;
+ break;
+ }
+
+ case ETCHTYPEB_EXECLIENTBASE:
+ { i_xxxx_client* iclient = (i_xxxx_client*) obj;
+ xxxx_client_impl* client_impl = (xxxx_client_impl*) iclient->thisx;
+ ETCH_ASSERT(client_impl && ((etch_object*)client_impl)->obj_type == ETCHTYPEB_EXECLIENTIMPL);
+ iobjsession = client_impl->iobjsession;
+ break;
+ }
+ }
+
+ return iobjsession;
+}
+
+/**
+ * etchstub_put_resultobj
+ * insert specified result object to message.
+ * @param replymsg the message, caller retains.
+ * @param resultobj the result object, caller relinquishes regardless of result.
+ * @return 0 success, -1 failure.
+ */
+int etchstub_put_resultobj (etch_stub* stub, etch_message* replymsg, etch_object* resultobj)
+{
+ int result = 0;
+ etch_field* key_result = builtins._mf_result;
+
+ /* check if the message already has a result object, i.e., an exception */
+ if (message_get (replymsg, key_result))
+ etch_object_destroy(resultobj); /* if so, discard the passed result */
+ else /* resultobj is relinquished here regardless of result */
+ result = message_put (replymsg, clone_field(key_result), (etch_object*) resultobj);
+
+ return result;
+}
+
+
+/**
+ * etchstub_send_reply
+ * private method invoked by all "stub helpers" (message logic implementation
+ * runners) for messages requiring a reply, in order to instantiate a reply
+ * message and transmit it back to sender.
+ */
+int etchstub_send_reply (etch_stub* stub, i_delivery_service* dsvc, etch_who* whofrom, etch_message* msg, etch_object* resultobj, boolean putResultObj)
+{
+ int result = 0;
+ const int is_exception_resobj = is_etch_exception(resultobj);
+
+ etch_type* newtype = NULL; /* see comment following */
+
+ /* instantiate the reply message
+ * note that if we are called for a one-way message, it is only if an
+ * exception was thrown by the implementation, and the exception is
+ * therefore to be the reply. in such a case, the etch compiler generates
+ * a message type as newtype above, which is the second parameter to
+ * message_reply(), in order that in_reply_to can be instantiated.
+ * (our example below makes newtype the 1-way exception reply type in
+ * this case). for two-way messages, newtype, and thus the second parameter
+ * to message_reply() is null.
+ */
+ etch_message* replymsg = message_reply (msg, newtype);
+
+ if (NULL == replymsg && is_exception_resobj)
+ { newtype = builtins._mt__exception;
+ replymsg = message_reply (msg, newtype);
+ }
+
+ if (NULL == replymsg) {
+ etch_type* replytype = newtype? newtype: message_type(msg);
+ char* logmask = "could not create reply message for type %s\n";
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, logmask, replytype->aname);
+ return -1;
+ }
+
+ if(putResultObj) {
+ result = etchstub_put_resultobj (stub, replymsg, resultobj);
+ if(result){
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "invalid return value.\n");
+ return result;
+ }
+ }
+
+ if (0 == result) { /* forward reply message for serialization */
+ result = dsvc->itm->transport_message (dsvc, whofrom, replymsg);
+ if(result){
+ etch_object_destroy(replymsg);
+ }
+ }
+
+ return result;
+}
+
+
+/**
+ * etchstub_validate_args
+ * private method invoked by all "stub helpers" to validate parameters
+ * to the individual stub helper and populate some objects for the helper.
+ */
+int etchstub_validate_args (etch_stub* stub, i_delivery_service* dsvc,
+ etch_message* msg, void* client, default_value_factory** vf,
+ void** vfimpl, void** climpl)
+{
+ int result = 0;
+ i_xxxx_client* iclient = NULL;
+ etch_object* clientimpl = NULL, *valufactimpl = NULL;
+ /* any assertion failure in this method indicates a coding error in etch core */
+ ETCH_ASSERT(stub && dsvc && client && msg && stub->params && vf && vfimpl && climpl);
+
+ iclient = (i_xxxx_client*) client;
+ clientimpl = iclient->thisx;
+ //HCG Check this for client!
+ //ETCH_ASSERT(is_etch_server_impl(clientimpl));
+ *climpl = clientimpl;
+
+ *vf = (default_value_factory*) stub->params->in_valufact;
+ ETCH_ASSERT(is_etch_valuefact(*vf));
+ valufactimpl = (*vf)->impl;
+ ETCH_ASSERT(is_etch_valuefactimpl(valufactimpl));
+ *vfimpl = valufactimpl;
+
+ return result;
+}
+
+
+/**
+ * etchstub_session_notify_ex()
+ *
+ * @param thisx the object which will receive an exception if any.
+ * may be the same object as _obj. caller retains.
+ *
+ * @param _obj caller retains. usually a xxxx_server_impl. may be null, or
+ * may be the same object as thisx. obj will belong to a known and limited set
+ * of classes, e.g. ETCHTYPEB_EXESERVERIMPL, so we can test _obj.obj_type
+ * if needed, and cast _obj to one of the mask objects (etch_svcobj_masks.h)
+ * such as xxxx_server_impl, in order to reference the maskable content.
+ *
+ * @param evt a notification event or a throwable exception, caller relinquishes.
+ *
+ * @return 0 success, -1 failure.
+ */
+int etchstub_session_notify_ex (void* thisx, void* _obj, etch_event* evt)
+{
+ int result = -1, is_evtparm_relinquished = FALSE;
+
+ /* this call arrives from type "stub helpers" implemented in xxxx_server_stub.
+ * these are thread procedures with arguments delivery service, some object
+ * _obj (possibly implementing objsession or throwable), who, and message,
+ * viz: int (*stubhelper) (stub, deliverysvc, _obj, sender, message);
+ * so from argument _obj, we extract the objsession interface and if present,
+ * call its _session_notify.
+ */
+
+ /* a server implementation (xxxx_server_impl) can request notifications of
+ * exceptions and the like by implementing and registering i_objsession
+ * callbacks. the presence of these function pointers in the implementation
+ * serves as the indicator of whether to forward the notification.
+ */
+ i_objsession* impl_callbacks = etchstub_get_session_callbacks_from (_obj);
+
+ etch_session_notify impl_session_notify = impl_callbacks?
+ impl_callbacks->_session_notify: NULL;
+
+ if (impl_session_notify) /* if impl has requested this event, forward it */
+ { /* event is relinquished to notify handlers by contract */
+ result = impl_session_notify (impl_callbacks, evt);
+ is_evtparm_relinquished = TRUE;
+ }else {
+ if(((xxxx_either_impl*)_obj)->_session_notify) {
+ result = ((xxxx_either_impl*)_obj)->_session_notify ((xxxx_either_impl*)_obj, evt);
+ is_evtparm_relinquished = TRUE;
+ }
+ }
+
+ if (0 != result)
+ {
+ char* logmask = impl_session_notify? logmask1: logmask2;
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, logmask, snstr);
+ }
+
+ if (!is_evtparm_relinquished)
+ {
+ etch_object_destroy(evt);
+ evt = NULL;
+ //ETCHOBJ_DESTROY(evt);
+ }
+
+ return result;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * stub helper threadpool execution support
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchstub_loghelper()
+ * convenience method to log entry or exit of a stub helper function.
+ */
+void etchstub_loghelper(opaque_stubhelper f, const int result, const int thread_id, const int is_start)
+{
+ if(is_start) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "start stub runner %x [%d]\n", f, thread_id);
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "exit stub runner %x (%d) [%d]\n", f, result, thread_id);
+ }
+}
+
+/**
+ * etchstub_proxy_threadproc()
+ * a thread procedure conforming to the signature expected by etch_threadpool,
+ * which accepts a stub parameter bundle as params.data, unbundles the parameters,
+ * and invokes the included stub helper function with those parameters.
+ */
+void etchstub_proxy_threadproc (void* data)
+{
+ etch_thread_params* tp = (etch_thread_params*)data;
+ int result = 0;
+ etchstub_runparams* p = tp? tp->data: NULL;
+ ETCH_ASSERT(p && p->run && (p->sig == ETCH_STUBPARAMS_SIGNATURE));
+ etchstub_loghelper (p->run, 0, tp->etch_thread_id, 1);
+
+ /* run "stub helper" procedure */
+ result = p->run (p->stub, p->ds, p->server, p->who, p->msg);
+
+ etchstub_loghelper (p->run, result, tp->etch_thread_id, 0);
+
+ //tp->etchobj->destroy(tp->etchobj);
+ etch_free(p);
+
+
+}
+
+
+/**
+ * new_etch_stubparams()
+ * bundle up stub runner parameters in order they can become
+ * etch_thread_params.data, passed in to the etchstub_proxy_threadproc.
+ */
+etchstub_runparams* new_etch_stubparams (etch_stub* stub, opaque_stubhelper runproc,
+ i_delivery_service* ds, i_xxxx_server* server, etch_who* whofrom, etch_message* msg)
+{
+ etchstub_runparams* p = etch_malloc(sizeof(etchstub_runparams), 0);
+ p->sig = ETCH_STUBPARAMS_SIGNATURE;
+ p->stub = stub;
+ p->run = runproc;
+ p->ds = ds;
+ p->who = whofrom;
+ p->msg = msg;
+ p->server = server;
+ return p;
+}
+
+
+/**
+ * etchstub_run()
+ * execute the service method helper function (stub helper). if no threadpool
+ * is specified, execute the helper function inline (on caller's thread);
+ * otherwise wrap the stub helper arguments and launch a thread on the supplied
+ * threadpool to execute the helper function.
+ * @param stub caller retains.
+ * @param runproc the service function for this message type.
+ * @param threadpool caller retains.
+ * @param server caller retains.
+ * @param who caller retains.
+ * @param msg caller retains.
+ * @return 0 success, -1 failure.
+ */
+int etchstub_run (etch_stub* stub, opaque_stubhelper runproc, etch_threadpool* threadpool,
+ i_xxxx_server* server, etch_who* who, etch_message* msg)
+{
+ int result = 0;
+ i_delivery_service* ds = stub->delivery_service;
+ ETCH_ASSERT(runproc && ds && server && msg);
+
+ if (threadpool)
+ {
+ /* a pointer to this parameter bundle will become etch_thread_params.data.
+ * etch_thread_params.is_own_data determines if the thread frees data on exit.
+ * is_own_data assumes the value of threadpool.is_free_data, which is true
+ * by default. so unless we have reset is_free_data on this pool, the pool
+ * thread will free this allocation at thread exit. and since also by default
+ * pool->is_data_etchobject is false, the deallocation will use etch_free;
+ */
+ etchstub_runparams* p = new_etch_stubparams(stub, runproc, ds, server, who, msg);
+ etch_thread* poolthread = NULL;
+ threadpool->is_manual_start = TRUE;
+
+ /* run stub helper function on a pool thread */
+ poolthread = threadpool->run (threadpool, etchstub_proxy_threadproc, p);
+
+ //poolthread->is_joined = TRUE;
+ //poolthread->on_exit()
+ poolthread->start(poolthread);
+// threadpool->is_manual_start = FALSE;
+
+ //if (poolthread) /* switch context and block on pool thread exit */
+ //{ const int id = poolthread->params.etch_thread_id;
+ // //etch_log(etchstub, etch_log_xdebug, "stub runner joining pool thread %d ...\n", id);
+ // etch_join (poolthread);
+ //}
+ //else result = -1;
+ }
+ else
+ { etchstub_loghelper(runproc, 0, 0, 1);
+
+ /* run stub helper function on this thread */
+ result = runproc (stub, ds, server, who, msg);
+
+ etchstub_loghelper(runproc, result, 0, 0);
+
+ }
+ return result;
+}
+
+
+/* - - - - - - - - - -
+ * i_sessionmessage
+ * - - - - - - - - - -
+ */
+
+/**
+ * etchstub_session_message()
+ * @param whofrom caller retains, can be null.
+ * @param msg caller relinquishes
+ * @return 0 (message handled), or -1 (error, closed, or timeout)
+ */
+int etchstub_session_message (void* data, etch_who* whofrom, etch_message* msg)
+{
+ etch_stub* thisx = (etch_stub*)data;
+ xxxx_either_stub* stubimpl = (xxxx_either_stub*) thisx->stubobj;
+ i_xxxx_server* serverimpl = NULL;
+ etch_session* sessionparams = NULL;
+ opaque_stubhelper stubhelper = NULL;
+ etch_threadpool* threadpool = NULL;
+ int result = -1, session_id = 0;
+ unsigned char async_mode = 0;
+
+ /* get the message type object associated with this message */
+ etch_type* thistype = message_type(msg);
+ ETCH_ASSERT(thistype);
+ ETCH_ASSERT(is_etch_stub(stubimpl));
+
+ session_id = stubimpl->owner_id;
+ do
+ {
+ /* get the message type's stub helper (runner) function.
+ * the stub helper is a pointer to a thread procedure function specific
+ * to the message type, which executes the associated service method.
+ */
+ if (NULL == (stubhelper = etchtype_get_type_stubhelper (thistype)))
+ { char* msgmask = "type '%s' missing stub runner procedure\n";
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, msgmask, thistype->aname);
+ break;
+ }
+
+ /* get the thread pool, if any, appropriate for the
+ * configured mode of execution for this message type
+ */
+ switch(async_mode = etchtype_get_async_mode (thistype))
+ {
+ //default: threadpool = thisx->params->qpool; break;
+ case ETCH_ASYNCMODE_QUEUED: threadpool = thisx->params->qpool; break;
+ case ETCH_ASYNCMODE_FREE: threadpool = thisx->params->fpool; break;
+ default: threadpool = NULL;
+ }
+
+
+ if(is_etch_client_stub(stubimpl)){
+ result = etchstub_run (thisx, stubhelper, threadpool, ((etch_client_factory*)thisx->params)->iclient, whofrom, msg);
+ }
+ else{
+
+ get_etch_session (thisx->params, session_id, &sessionparams);
+ ETCH_ASSERT (sessionparams);
+ serverimpl = sessionparams->server;
+
+ /* execute the service method execution function (stub helper),
+ * either on a thread or inline.
+ */
+ result = etchstub_run (thisx, stubhelper, threadpool, serverimpl, whofrom, msg);
+
+ }
+ } while(0);
+ /* this is the end of the line for the message */
+
+
+ return result;
+}
+
+
+/**
+ * etchstub_session_control()
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+int etchstub_session_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_stub* thisx = (etch_stub*)data;
+ int result = -1, is_params_relinquished = FALSE;
+
+ /* a server implementation (xxxx_server_impl) can request notifications of
+ * exceptions and the like by implementing and registering i_objsession
+ * callbacks. the presence of these function pointers in the implementation
+ * serves as the indicator of whether to forward the notification.
+ */
+ i_objsession* impl_callbacks = thisx->impl_callbacks;
+
+ etch_session_control impl_session_control = impl_callbacks?
+ impl_callbacks->_session_control: NULL;
+
+ if (impl_session_control) /* if impl has requested this event, forward it */
+ if (0 == (result = impl_session_control (impl_callbacks, control, value)))
+ is_params_relinquished = TRUE;
+
+ if (0 != result)
+ { char* logmask = impl_session_control? logmask1: logmask2;
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, logmask, scstr);
+ }
+
+ if (!is_params_relinquished)
+ {
+ etch_object_destroy(control);
+ control = NULL;
+
+ etch_object_destroy(value);
+ value = NULL;
+ }
+
+ return result;
+}
+
+
+/**
+ * etch_etchstub_session_notify()
+ * @param evt event, caller relinquishes.
+ */
+int etchstub_session_notify(void* data, etch_event* evt)
+{
+ etch_stub* thisx = (etch_stub*)data;
+ const int result = etchstub_session_notify_ex (thisx, thisx->obj, evt);
+ return result;
+}
+
+
+/**
+ * etch_etchstub_session_query()
+ * @param query, caller relinquishes.
+ */
+etch_object* etchstub_session_query (void* data, etch_query* query)
+{
+ etch_stub* thisx = (etch_stub*)data;
+ etch_object* resultobj = NULL;
+ int is_params_relinquished = FALSE;
+
+ /* a server implementation (xxxx_server_impl) can request notifications of
+ * exceptions and the like by implementing and registering i_objsession
+ * callbacks. the presence of these function pointers in the implementation
+ * serves as the indicator of whether to forward the notification.
+ */
+ i_objsession* impl_callbacks = thisx->impl_callbacks;
+
+ etch_session_query impl_session_query = impl_callbacks?
+ impl_callbacks->_session_query: NULL;
+
+ if (impl_session_query) /* if impl has requested this event, forward it */
+ if (NULL != (resultobj = impl_session_query (impl_callbacks, query)))
+ is_params_relinquished = TRUE;
+
+ if (NULL == resultobj)
+ { char* logmask = impl_session_query? logmask1: logmask2;
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, logmask, sqstr);
+ }
+
+ if (!is_params_relinquished) {
+ etch_object_destroy(query);
+ query = NULL;
+ }
+
+ return resultobj;
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * constructors, destructor
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * destroy_stub_object()
+ * stub implementation private destructor.
+ * calls back to hand-coded destructor for possible custom deallocation,
+ * then destroys the stub base, including the shared session interface.
+ */
+int destroy_stub_object (void* thisx)
+{
+ xxxx_either_stub* stubobj = NULL;
+ if (NULL == thisx) return -1;
+
+ stubobj = (xxxx_either_stub*) thisx;
+ if (!is_etch_stub(thisx)) return -1;
+
+ if (!is_etchobj_static_content(stubobj))
+ {
+ if (stubobj->destroyex) /* call back to user dtor */
+ stubobj->destroyex(thisx);
+
+ if (stubobj->stub_base)
+ etch_object_destroy(stubobj->stub_base);
+ }
+
+ return destroy_objectex((etch_object*)stubobj);
+}
+
+
+/**
+ * is_etch_stub()
+ */
+int is_etch_stub(void* x)
+{
+ int result = FALSE, objtype = x? ((etch_object*)x)->obj_type: 0;
+ switch(objtype)
+ { case ETCHTYPEB_STUB: case ETCHTYPEB_CLIENTSTUB: case ETCHTYPEB_SERVERSTUB:
+ result = TRUE;
+ }
+ return result;
+}
+
+
+/**
+ * new_stubimpl_init()
+ * generic stub implementation constructor
+ * @param implobj i_xxxx_client* or i_xxxx_server*.
+ * @param objsize number of bytes in the stub object implementation.
+ * all we know about here is the mask, or object header if you will.
+ * @param stubtype ETCH_STUBTYPE_CLIENT or ETCH_STUBTYPE_SERVER.
+ * @param userdtor a hand-coded callback in the implementation conforming
+ * to signature etch_destructor, for the purpose of freeing any custom
+ * memory allocations.
+ * @param ids the delivery service, caller retains.
+ * @param qp the queued thread pool, optional, caller retains.
+ * @param fp the free thread pool, optional, caller retains.
+ * @param params a etch_server_factory* parameter bundle, caller retains.
+ * if it is always the case that this parameter is present and is a
+ * etch_server_factory*, we can lose the ids, qp, and fp ctor parameters.
+ */
+void* new_stubimpl_init (void* implobj, const int objsize,
+ const unsigned char stubtype, etch_object_destructor userdtor,
+ i_delivery_service* ids, etch_threadpool* qp, etch_threadpool* fp,
+ void* params)
+{
+ unsigned short obj_type, class_id;
+ xxxx_either_stub* newstub = NULL;
+
+ switch(stubtype)
+ {
+ case ETCH_STUBTYPE_CLIENT:
+ obj_type = ETCHTYPEB_CLIENTSTUB;
+ class_id = CLASSID_CLIENTSTUB;
+ break;
+ case ETCH_STUBTYPE_SERVER:
+ obj_type = ETCHTYPEB_SERVERSTUB;
+ class_id = CLASSID_SERVERSTUB;
+ break;
+ default: return NULL;
+ }
+
+ newstub = (xxxx_either_stub*) new_object (objsize, obj_type, class_id);
+ ((etch_object*)newstub)->destroy = destroy_stub_object; /* private */
+ newstub->destroyex = userdtor; /* public */
+ newstub->stub_base = new_stub (implobj, stubtype, ids, qp, fp);
+ newstub->stub_base->stubobj = (etch_object*) newstub;
+ newstub->stub_base->params = params;
+
+ return newstub;
+}
+
+
+/**
+ * new_clientstub_init()
+ * generic client stub implementation constructor.
+ * @param bytelen number of bytes in the client stub object implementation.
+ * all we know about here is the mask, or object header if you will.
+ * @param dtor a hand-coded callback in the implementation conforming
+ * to signature etch_destructor, for the purpose of freeing any custom
+ * memory allocations.
+ * @param ids the delivery service, caller retains.
+ * @param qp the queued thread pool, optional, caller retains.
+ * @param fp the free thread pool, optional, caller retains.
+ * @param params a etch_server_factory* parameter bundle, caller retains.
+ * if it is always the case that this parameter is present and is a
+ * etch_server_factory*, we can lose the ids, qp, and fp ctor parameters.
+ */
+void* new_clientstub_init (void* implobj, const int bytelen, etch_object_destructor dtor,
+ i_delivery_service* ids, etch_threadpool* qp, etch_threadpool* fp, void* params)
+{
+ return new_stubimpl_init (implobj, bytelen, ETCH_STUBTYPE_CLIENT,
+ dtor, ids, qp, fp, params);
+}
+
+
+/**
+ * new_serverstub_init()
+ * generic server stub implementation constructor.
+ * @param bytelen number of bytes in the server stub object implementation.
+ * all we know about here is the mask, or object header if you will.
+ * @param dtor a hand-coded callback in the implementation conforming
+ * to signature etch_destructor, for the purpose of freeing any custom
+ * memory allocations.
+ * @param ids the delivery service, caller retains.
+ * @param qp the queued thread pool, optional, caller retains.
+ * @param fp the free thread pool, optional, caller retains.
+ * @param params a etch_server_factory* parameter bundle, caller retains.
+ * if it is always the case that this parameter is present and is a
+ * etch_server_factory*, we can lose the ids, qp, and fp ctor parameters.
+ */
+void* new_serverstub_init (void* implobj, const int bytelen, etch_object_destructor dtor,
+ i_delivery_service* ids, etch_threadpool* qp, etch_threadpool* fp, void* params)
+{
+ return new_stubimpl_init (implobj, bytelen, ETCH_STUBTYPE_SERVER,
+ dtor, ids, qp, fp, params);
+}
+
+
+
diff --git a/binding-c/runtime/c/src/main/support/etch_transportint.c b/binding-c/runtime/c/src/main/support/etch_transportint.c
new file mode 100644
index 0000000..42bd910
--- /dev/null
+++ b/binding-c/runtime/c/src/main/support/etch_transportint.c
@@ -0,0 +1,130 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_transportint.c
+ * transport interface
+ */
+
+#include "etch_transportint.h"
+#include "etch_sessionint.h"
+#include "etch_mem.h"
+#include "etch_session_message.h"
+
+int etchtransport_def_transport_control (void*, etch_event*, etch_object*);
+int etchtransport_def_transport_notify (void*, etch_event*);
+etch_object* etchtransport_def_transport_query (void*, etch_query*);
+
+i_session* etchtransport_def_get_session(void* thisx);
+void etchtransport_def_set_session(void* thisx, void* session);
+
+
+/**
+ * new_default_transport_interface
+ * return a transport interface populated with defaults for virtuals.
+ * caller owns returned object, not an etch object, use etch_free() to destroy.
+ */
+i_transport* new_default_transport_interface(void* thisx)
+{
+ i_transport* newi = etch_malloc(sizeof(i_transport), ETCHTYPEB_RAWOBJECT);
+
+ newi->transport_control = etchtransport_def_transport_control;
+ newi->transport_notify = etchtransport_def_transport_notify;
+ newi->transport_query = etchtransport_def_transport_query;
+ newi->get_session = etchtransport_def_get_session;
+ newi->set_session = etchtransport_def_set_session;
+ newi->thisx = thisx;
+ return newi;
+}
+
+
+/**
+ * new_transport_interface
+ * return a transport interface populated with specified virtuals.
+ * caller owns returned object, not an etch object, use etch_free() to destroy.
+ */
+i_transport* new_transport_interface(void* thisx,
+ etch_transport_control sc, etch_transport_notify sn, etch_transport_query sq)
+{
+ i_transport* newi = new_default_transport_interface(thisx);
+
+ if (sc) newi->transport_control = sc;
+ if (sn) newi->transport_notify = sn;
+ if (sq) newi->transport_query = sq;
+ return newi;
+}
+
+
+/**
+ * new_transport_interface_ex
+ * return a transport interface populated with specified virtuals.
+ * caller owns returned object, not an etch object, use etch_free() to destroy.
+ */
+i_transport* new_transport_interface_ex(void* thisx,
+ etch_transport_control sc, etch_transport_notify sn, etch_transport_query sq,
+ etch_transport_get_session gs, etch_transport_set_session ss)
+{
+ i_transport* newi = new_transport_interface(thisx, sc, sn, sq);
+
+ if (gs) newi->get_session = gs;
+ if (ss) newi->set_session = ss;
+ return newi;
+}
+
+
+/**
+ * clone_transport()
+ */
+i_transport* clone_transport(void* thisx, const i_transport* thattransport)
+{
+ i_transport* newtransport
+ = thattransport? new_default_transport_interface(thisx): NULL;
+
+ if (newtransport)
+ memcpy(newtransport, thattransport, sizeof(i_transport));
+
+ return newtransport;
+}
+
+
+int etchtransport_def_transport_control (void* obj, etch_event* evt, etch_object* v)
+{
+ return -1;
+}
+
+int etchtransport_def_transport_notify (void* obj, etch_event* evt)
+{
+ return -1;
+}
+
+
+etch_object* etchtransport_def_transport_query (void* obj, etch_query* query)
+{
+ return NULL;
+}
+
+
+i_session* etchtransport_def_get_session(void* thisx)
+{
+ return NULL;
+}
+
+
+void etchtransport_def_set_session(void* thisx, void* session)
+{
+}
diff --git a/binding-c/runtime/c/src/main/transport/etch_connection.c b/binding-c/runtime/c/src/main/transport/etch_connection.c
new file mode 100644
index 0000000..2c5c2b7
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_connection.c
@@ -0,0 +1,243 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_connection.c
+ * connection client and server classes - tcp, udp
+ */
+#include "etch.h"
+#include "etch_runtime.h"
+#include "etch_thread.h"
+#include "etch_connection.h"
+#include "etch_encoding.h"
+#include "etch_flexbuffer.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+static const char* LOG_CATEGORY = "etch_connection";
+
+// extern types
+extern etch_pool_t* g_etch_main_pool;
+extern apr_thread_mutex_t* g_etch_main_pool_mutex;
+
+const wchar_t* ETCH_CONNECTION_RECONDELAY = L"TcpConnection.reconnect_delay";
+const wchar_t* ETCH_CONNECTION_AUTOFLUSH = L"TcpConnection.autoFlush";
+const wchar_t* ETCH_CONNECTION_KEEPALIVE = L"TcpConnection.keepAlive";
+const wchar_t* ETCH_CONNECTION_LINGERTIME = L"TcpConnection.lingerTime";
+const wchar_t* ETCH_CONNECTION_NODELAY = L"TcpConnection.noDelay";
+const wchar_t* ETCH_CONNECTION_TRAFCLASS = L"TcpConnection.trafficClass";
+const wchar_t* ETCH_CONNECTION_BUFSIZE = L"TcpConnection.bufferSize";
+const wchar_t* ETCH_TCPLISTENER_BACKLOG = L"TcpListener.backlog";
+
+unsigned next_etch_connection_id();
+
+unsigned connection_id_farm;
+
+/*
+ * is_good_tcp_params()
+ */
+int is_good_tcp_params(etch_url* url, void* resources, etch_rawsocket* socket)
+{
+ int whicherr = 0;
+
+ if (NULL == socket)
+ {
+ if (NULL == url->host) whicherr = 0x1;
+ if (url->port <= 0 || url->port > 0xffff) whicherr |= 0x2;
+ }
+
+ return whicherr == 0;
+}
+
+
+/*
+ * etch_socket_set_timeout
+ * set timeout for specified socket
+ */
+int etch_socket_set_timeout(etch_rawsocket* socket, const unsigned ms)
+{
+ const int result = apr_socket_timeout_set(socket, ms * 1000);
+ return 0 == result? 0: -1;
+}
+
+
+/*
+ * etch_socket_get_timeout
+ * get timeout set on specified socket
+ */
+int etch_socket_get_timeout(etch_rawsocket* socket, unsigned* retms)
+{
+ int64 val = 0;
+ if (0 != apr_socket_timeout_get(socket, &val)) return -1;
+ if (retms) *retms = (unsigned) (val / 1000);
+ return 0;
+}
+
+
+/**
+ * etchconx_set_socket_options()
+ */
+int etchconx_set_socket_options(void *c)
+{
+ return 0;
+}
+
+
+/**
+ * next_etch_connection_id()
+ * return a unique ID used to identify a connection instance
+ */
+unsigned next_etch_connection_id()
+{
+ do { apr_atomic_inc32 ((volatile apr_uint32_t*) &connection_id_farm);
+ } while(connection_id_farm == 0);
+
+ return connection_id_farm;
+}
+
+
+/**
+ * etch_defconx_on_data()
+ * default receive data handler
+ */
+int etch_defconx_on_data (void* conx, const int unused, int length, void* voidData)
+{
+ char* msgmask = "connxn %d no data handler provided for %d bytes\n";
+ etch_connection* cx = (etch_connection*) conx;
+ ETCH_ASSERT(is_etch_connection(cx));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_WARN, msgmask, cx->conxid, length);
+ return 0;
+}
+
+
+/*
+ * etch_defconx_on_event()
+ * default handler for connection events
+ */
+int etch_defconx_on_event (void* c, const int e, int p1, void* p2)
+{
+ return 0;
+}
+
+/*
+ * etchconx_wait_up()
+ * block until connection comes up or timeout
+ */
+int etchconx_wait_up(etch_connection* cx, int timeoutms)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ status = etch_wait_timedwait(cx->wait_up, ETCH_CONXEVT_UP, timeoutms);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * etchconx_wait_down()
+ * block until connection is closed or timeout
+ */
+int etchconx_wait_down(etch_connection* cx, int timeoutms)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ status = etch_wait_timedwait(cx->wait_down, ETCH_CONXEVT_DOWN, timeoutms);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * etch_destroy_connection()
+ * free memory owned by connection.
+ */
+int etch_destroy_connection(etch_connection* cx)
+{
+ if (cx)
+ {
+ etch_wait_destroy(cx->wait_up);
+ etch_wait_destroy(cx->wait_down);
+ etch_mutex_destroy(cx->mutex);
+ etch_free(cx->hostname);
+ /* free apr subpool */
+ //printf("5 destroying apr pool %p\n",cx->aprpool);
+ apr_pool_destroy(cx->aprpool);
+
+ }
+ return 0;
+}
+
+/*
+ * etch_init_connection()
+ * initialize an etch_connection (abstract base)
+ */
+int etch_init_connection (etch_connection* cx, etch_rawsocket* socket, void* owner)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ memset(cx, 0, sizeof(etch_connection));
+
+ cx->conxid = next_etch_connection_id();
+ cx->owner = owner;
+
+ // TODO: pool handling
+
+ /* set memory pool used for APR objects associated with this connection.
+ * if the connection is being created with an existing socket, use the
+ * memory pool already assigned to it, otherwise partition a new subpool.
+ * if connection allocates a subpool, connection will free it on destroy.
+ */
+ if (socket) {
+ cx->aprpool = apr_socket_pool_get(socket);
+ }
+
+ if (cx->aprpool == NULL) {
+ // TODO: pool
+ apr_pool_create(&cx->aprpool, g_etch_main_pool);
+ //printf("3 creating apr pool %p\n",cx->aprpool);
+ /* set pool abort function */
+ apr_pool_abort_set(etch_runtime_mem_abort, cx->aprpool);
+
+ cx->is_ownpool = TRUE;
+ }
+
+ if (cx->aprpool == NULL)
+ { cx->aprpool = g_etch_main_pool;
+ cx->is_ownpool = FALSE;
+ }
+
+ cx->sig = ETCH_CONX_SIG;
+
+ // TODO: pool
+ status = etch_mutex_create(&cx->mutex, ETCH_MUTEX_NESTED, NULL);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ // TODO: pool
+ status = etch_wait_create(&cx->wait_up, NULL);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ // TODO: pool
+ status = etch_wait_create(&cx->wait_down, NULL);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+
+ cx->bufsize = ETCH_CONX_DEFAULT_BUFSIZE;
+ cx->on_event = etch_defconx_on_event;
+ cx->on_data = etch_defconx_on_data;
+ cx->set_socket_options = etchconx_set_socket_options;
+ return 0;
+}
+
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_connection_event.c b/binding-c/runtime/c/src/main/transport/etch_connection_event.c
new file mode 100644
index 0000000..251cee4
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_connection_event.c
@@ -0,0 +1,336 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_conxevent.c
+ * event handler overrides for the various connection superclasses
+ */
+
+#include "etch_thread.h"
+#include "etch_tcp_connection.h"
+#include "etch_tcp_server.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+
+static const char* LOG_CATEGORY = "etch_connection_event";
+
+/*
+ * etch_deftcplistener_on_event()
+ * default handler for listener events
+ */
+int etch_deftcplistener_on_event(etch_tcp_server* l, etch_tcp_connection* c, const int e, int p1, void* p2)
+{
+ char cxstr[24], *smask = "server %u";
+
+ if (c)
+ { c->cx.listener = (etch_object*) l;
+ return c->cx.on_event(c, e, p1, p2);
+ }
+
+ apr_snprintf(cxstr, sizeof(cxstr), smask, l->listener_id);
+
+ switch(e)
+ {
+ case ETCH_CONXEVT_CREATED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s created\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_CREATERR:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s not created\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_OPENING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s opening ...\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_DESTROYING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s destroying ...\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_DESTROYED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s destroyed\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_SHUTDOWN:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_WARN, "%s shutdown request detected\n", cxstr);
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * etch_tcpconx_on_event()
+ * default handler for connection events
+ */
+int etch_tcpconx_on_event(void* data, const int e, int p1, void* p2)
+{
+ etch_tcp_connection* c = (etch_tcp_connection*)data;
+ int result = 0, lid = 0;
+ char cxstr[32], estr[128];
+ char *scmask = "server %u connxn %u", *cmask = "connxn %u";
+
+ if (is_etch_tcpserver(c->cx.listener))
+ lid = ((etch_tcp_server*)(c->cx.listener))->listener_id;
+ if (lid)
+ sprintf(cxstr, scmask, lid, c->cx.conxid);
+ else sprintf(cxstr, cmask, c->cx.conxid);
+
+ switch(e)
+ {
+ case ETCH_CONXEVT_RECEIVING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "%s begin receive (block) ...\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_RECEIVERR:
+ apr_strerror(p1, estr, 128);
+ if(IS_ETCH_SOCKET_TIMEOUT(p1))
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s socket receive timed out\n", cxstr);
+ else
+ if(p1 == APR_OTHER_END_CLOSED)
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_WARN,"%s connection was broken\n", cxstr);
+ else
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s apr_socket_recv() %s\n", cxstr, estr);
+ break;
+
+ case ETCH_CONXEVT_RECEIVED: /* "eod=%d", p1 */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s end receive %d bytes\n", cxstr, (int)(size_t)p2);
+ break;
+
+ case ETCH_CONXEVT_CONXCLOSED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s local connection closed\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_PEERCLOSED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s peer connection closed\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_RECEIVEND:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "%s exit receive loop\n", cxstr, p1);
+ break;
+
+ case ETCH_CONXEVT_SENDING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "%s begin send\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_SENDERR:
+ apr_strerror(p1, estr, 128);
+ if(IS_ETCH_SOCKET_TIMEOUT(p1))
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s socket send timed out\n", cxstr);
+ else
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s apr_socket_send() %s\n", cxstr, estr);
+ break;
+
+ case ETCH_CONXEVT_SENDEND:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s end send %d bytes\n", cxstr, p1);
+ break;
+
+ case ETCH_CONXEVT_RCVPUMP_START:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s start receive pump\n", cxstr, p1);
+ break;
+
+ case ETCH_CONXEVT_RCVPUMP_RECEIVING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s thread %d receiving ...\n", cxstr, p1);
+ break;
+
+ case ETCH_CONXEVT_RCVPUMP_ERR:
+ result = -1;
+ if(p1 == APR_OTHER_END_CLOSED)
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_WARN, "%s connection was broken\n", cxstr);
+ else
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s receive failed\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_RCVPUMP_STOP:
+ result = p1;
+ if (result >= 0)
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s receive pump on thread %d exited\n", cxstr, (int) (size_t) p2);
+ else
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s receive pump abnormal exit\n", cxstr);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s destroying accepted connection\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_ACCEPTING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s accepting ...\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_ACCEPTERR:
+ result = -1;
+ apr_strerror((apr_status_t)(size_t)p2, estr, 128);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s apr_socket_accept() %s\n", cxstr, estr);
+ break;
+
+ case ETCH_CONXEVT_ACCEPTED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s accepted\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_CREATED:
+ if(p1)
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s created for socket %x\n", cxstr, p1);
+ else
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s created\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_CREATERR:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s not created\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_OPENING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s opening ...\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_OPENERR:
+ result = -1;
+
+ switch(p1)
+ {
+ case 0:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s not opened\n", cxstr);
+ break;
+ case 2:
+ apr_strerror((apr_status_t)(size_t)p2, estr, 128);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_WARN, "%s socket connect: %s\n", cxstr, estr);
+ break;
+ case 3:
+ apr_strerror((apr_status_t)(size_t)p2, estr, 128);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,
+ "%s socket create: %s\n", cxstr, estr);
+ break;
+ case 4:
+ apr_strerror((apr_status_t)(size_t)p2, estr, 128);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,
+ "%s sockaddr info: %s\n", cxstr, estr);
+ break;
+ case 5:
+ apr_strerror((apr_status_t)(size_t)p2, estr, 128);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,
+ "%s socket bind: %s\n", cxstr, estr);
+ break;
+ case 6:
+ apr_strerror((apr_status_t)(size_t)p2, estr, 128);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s socket listen: %s\n", cxstr, estr);
+ break;
+ }
+ break;
+
+ case ETCH_CONXEVT_OPENED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s opened\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_STARTING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s starting ...\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_STARTERR:
+ result = -1;
+ switch(p1)
+ {
+ case 0:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s not started\n", cxstr);
+ break;
+ case 1:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s etch_threadpool.run()\n", cxstr);
+ break;
+ }
+ break;
+
+ case ETCH_CONXEVT_STARTED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s started\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_ACCEPTPUMPEXIT:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s accept pump on thread %d exited\n", cxstr, p1);
+ break;
+
+ case ETCH_CONXEVT_ACCEPTPUMPEXITERR:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s accept pump abnormal exit\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_LISTENED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s server id is %d\n", cxstr, p1);
+ break;
+
+ case ETCH_CONXEVT_UP:
+ case ETCH_CONXEVT_DOWN:
+ break;
+
+ case ETCH_CONXEVT_SOCKOPTERR:
+ result = 1; /* to increment counter */
+ apr_strerror(p1, estr, 128);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s set socket option %s: %s\n", cxstr, p2, estr);
+ break;
+
+ case ETCH_CONXEVT_SHUTDOWN:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_WARN, "%s shutdown request detected\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_STOPPING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s stopping ...\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_STOPERR:
+ result = -1;
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s not stopped\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_STOPPED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s stopped\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_CLOSING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s closing ...\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_CLOSERR:
+ result = -1;
+
+ switch(p1)
+ {
+ case 0:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s not closed\n", cxstr);
+ break;
+ case 1:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s close when not open\n", cxstr);
+ break;
+ case 2:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s concurrent close denied\n", cxstr);
+ break;
+ case 3:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,
+ "%s apr_socket_close() error %d\n", cxstr, (int)(size_t)p2);
+ break;
+ }
+ break;
+
+ case ETCH_CONXEVT_CLOSED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s closed\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_DESTROYING:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s destroying ...\n", cxstr);
+ break;
+
+ case ETCH_CONXEVT_DESTROYED:
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "%s destroyed\n", cxstr);
+ break;
+ }
+
+ return result;
+}
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_mailbox_manager.c b/binding-c/runtime/c/src/main/transport/etch_mailbox_manager.c
new file mode 100644
index 0000000..2773cee
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_mailbox_manager.c
@@ -0,0 +1,120 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_mailboxmgr.c
+ * i_mailbox manager interface
+ */
+
+#include "etch_mailbox_manager.h"
+#include "etch_message.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+/*
+static const char* LOG_CATEGORY = "etch_mailbox_manager";
+*/
+
+int destroy_mailbox_manager_interface(void*);
+int etchmailboxmgr_def_headersize(i_mailbox_manager*);
+int etchmailboxmgr_def_transport_call(i_mailbox_manager*, etch_who*, etch_message*, i_mailbox**);
+int etchmailboxmgr_def_redeliver (void*, etch_who*, etch_message*);
+int etchmailboxmgr_def_unregister(void*, i_mailbox*);
+
+
+/**
+ * new_mailboxmgr_interface()
+ * i_mailbox_manager constructor.
+ * @param thisx the mailbox manager object.
+ * @param itm transportmesssage interface, caller retains, can be null.
+ * @param ism sessionmessage interface, caller retains, can be null.
+ */
+i_mailbox_manager* new_mailboxmgr_interface(void* thisx,
+ i_transportmessage* itm, i_sessionmessage* ism)
+{
+ i_mailbox_manager* newi = (i_mailbox_manager*) new_object(sizeof(i_mailbox_manager), ETCHTYPEB_MBOX_MANAGER, CLASSID_MBOX_MANAGER);
+
+ newi->thisx = thisx;
+ ((etch_object*)newi)->clone = clone_null;
+ ((etch_object*)newi)->destroy = destroy_mailbox_manager_interface;
+
+ newi->transport_call = (etch_mbm_transport_call)etchmailboxmgr_def_transport_call;
+ newi->redeliver = etchmailboxmgr_def_redeliver;
+ newi->unregister = etchmailboxmgr_def_unregister;
+
+ newi->itm = itm? itm: new_transportmsg_interface(newi, NULL, NULL);
+ newi->transport_message = newi->itm->transport_message;
+ newi->transport_message = newi->itm->transport_message;
+ newi->transport_control = newi->itm->transport_control;
+ newi->transport_notify = newi->itm->transport_notify;
+ newi->transport_query = newi->itm->transport_query;
+ newi->get_session = newi->itm->get_session;
+ newi->set_session = newi->itm->set_session;
+
+ return newi;
+}
+
+
+/**
+ * destroy_mailbox_manager_interface()
+ * i_mailbox_manager destructor
+ */
+int destroy_mailbox_manager_interface(void* data)
+{
+ i_mailbox_manager* mgr = (i_mailbox_manager*)data;
+ if (NULL == mgr) return -1;
+
+ if (!is_etchobj_static_content(mgr))
+ {
+ etch_object_destroy(mgr->ism);
+
+ etch_object_destroy(mgr->itm);
+ }
+
+ return destroy_objectex((etch_object*)mgr);
+}
+
+
+/**
+ * etchmailboxmgr_def_transport_call()
+ * default implementation of i_mailbox_manager::transport_call()
+ */
+int etchmailboxmgr_def_transport_call(i_mailbox_manager* imbm, etch_who* whoto, etch_message* msg, i_mailbox** out)
+{
+ return -1;
+}
+
+
+/**
+ * etchmailboxmgr_def_redeliver()
+ * default implementation of i_mailbox_manager::redeliver()
+ */
+int etchmailboxmgr_def_redeliver(void* mailbox, etch_who* whofrom, etch_message* msg)
+{
+ return -1;
+}
+
+
+/**
+ * etchmailboxmgr_def_unregister()
+ * default implementation of i_mailbox_manager::unregister()
+ */
+int etchmailboxmgr_def_unregister(void* imbm, i_mailbox* mbox)
+{
+ return -1;
+}
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_messagizer.c b/binding-c/runtime/c/src/main/transport/etch_messagizer.c
new file mode 100644
index 0000000..1036669
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_messagizer.c
@@ -0,0 +1,470 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_msgizer.c
+ * messagizer accepts packets and translates them to messages,
+ * and it accepts messages and translates them to packets.
+ */
+
+#include "etch_messagizer.h"
+#include "etch_tdformat.h"
+#include "etch_thread.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+
+static const char* LOG_CATEGORY = "etch_messagizer";
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+int destroy_messagizer(void*);
+int etch_msgizer_transport_message(void*, void*, void*);
+int etch_msgizer_session_packet (void*, void*, void*);
+int etch_msgizer_session_control (void*, etch_event*, etch_object*);
+int etch_msgizer_session_notify (void*, etch_event*);
+etch_object* etch_msgizer_session_query (void*, etch_query*);
+int etch_msgizer_transport_control(void*, etch_event*, etch_object*);
+int etch_msgizer_transport_notify (void*, etch_event*);
+etch_object* etch_msgizer_transport_query (void*, etch_query*);
+
+
+/* - - - - - - - - - - - - - - -
+ * etch_messagizer
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_messagizer()
+ * etch_messagizer public constructor
+ * @param ixport transport interface owned by caller
+ * @param uri a URI string owned by caller.
+ * @param resx a resources map owned by caller.
+ */
+
+etch_messagizer* new_messagizer (i_transportpacket* ixp, wchar_t* uri, etch_resources* resx)
+{
+ etch_url* url = new_url(uri);
+
+ etch_messagizer* messagizer = new_messagizer_a(ixp, url, resx);
+
+ etch_object_destroy(url);
+ return messagizer;
+}
+
+
+/**
+ * new_messagizer_a()
+ * etch_messagizer private constructor
+ * @param ipacket transport interface owned by caller
+ * @param uri a URI string owned by caller.
+ * @param resxmap a resources map owned by caller.
+ */
+etch_messagizer* new_messagizer_a (i_transportpacket* ixp, etch_url* url, etch_resources* resxmap)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ tagdata_format_factory* formatfactory = NULL;
+ etch_messagizer* messagizer = NULL;
+ i_transport* itransport = NULL;
+ etch_value_factory* vf = NULL;
+ etch_string* formatobj = NULL;
+ i_session* isession = NULL;
+ etch_mutex* mutex = NULL;
+ int result = -1;
+
+ do
+ {
+ formatobj = (etch_string*) etchurl_get_term (url, ETCH_RESXKEY_MSGIZER_FORMAT);
+
+ if (NULL == formatobj && NULL != resxmap)
+ formatobj = (etch_string*) etch_resources_get (resxmap, ETCH_RESXKEY_MSGIZER_FORMAT);
+
+ if (NULL == formatobj) {
+ ETCH_LOGW(LOG_CATEGORY, ETCH_LOG_ERROR, L"URI missing format specifier '%s'\n", ETCH_RESXKEY_MSGIZER_FORMAT);
+ break;
+ }
+
+ formatfactory = get_format_factory(formatobj->v.valw);
+ if (NULL == formatfactory) {
+ ETCH_LOGW(LOG_CATEGORY, ETCH_LOG_ERROR, L"no format '%s' is installed\n",
+ formatobj->v.valw);
+ break;
+ }
+
+ vf = resxmap? (etch_value_factory*) etch_resources_get(resxmap,
+ ETCH_RESXKEY_MSGIZER_VALUFACT): NULL;
+ if (NULL == vf)
+ {
+ ETCH_LOGW(LOG_CATEGORY, ETCH_LOG_ERROR, L"no value factory '%s' is installed\n", ETCH_RESXKEY_MSGIZER_VALUFACT);
+ break;
+ }
+
+ // TODO: pool
+ status = etch_mutex_create(&mutex, ETCH_MUTEX_NESTED, NULL);
+ if(status != ETCH_SUCCESS) {
+ // error
+ break;
+ }
+
+ /* - - - - - - - - - - - - - - -
+ * etch_messagizer
+ * - - - - - - - - - - - - - - -
+ */
+ messagizer = (etch_messagizer*) new_object(sizeof(etch_messagizer), ETCHTYPEB_MESSAGIZER, CLASSID_MESSAGIZER);
+
+ ((etch_object*)messagizer)->destroy = destroy_messagizer;
+ ((etch_object*)messagizer)->clone = clone_null;
+ messagizer->msglock = mutex;
+ messagizer->msgbuf = new_flexbuffer(ETCH_DEFSIZE); /* 2K default */
+
+ /* set our transport to that of next lower layer (packetizer) */
+ messagizer->transport = ixp; /* i_transportpacket owned by caller */
+
+ /* - - - - - - - - - - - - - - -
+ * i_transportmessage
+ * - - - - - - - - - - - - - - -
+ */
+ itransport = new_transport_interface_ex (messagizer,
+ (etch_transport_control) etch_msgizer_transport_control,
+ (etch_transport_notify) etch_msgizer_transport_notify,
+ (etch_transport_query) etch_msgizer_transport_query,
+ etch_msgizer_get_session,
+ etch_msgizer_set_session);
+
+ /* instantiate i_transportmessage interface which messagizer implements */
+ messagizer->transportmsg = new_transportmsg_interface(messagizer,
+ etch_msgizer_transport_message,
+ itransport); /* transportmsg now owns itransport */
+
+ /* copy i_transportmessage interface methods up to messagizer */
+ messagizer->transport_message = etch_msgizer_transport_message;
+ messagizer->transport_control = itransport->transport_control;
+ messagizer->transport_notify = itransport->transport_notify;
+ messagizer->transport_query = itransport->transport_query;
+ messagizer->get_session = itransport->get_session;
+ messagizer->set_session = itransport->set_session;
+
+ /* - - - - - - - - - - - - - - -
+ * i_sessionpacket
+ * - - - - - - - - - - - - - - -
+ */
+ isession = new_session_interface(messagizer,
+ (etch_session_control) etch_msgizer_session_control,
+ (etch_session_notify) etch_msgizer_session_notify,
+ (etch_session_query) etch_msgizer_session_query);
+
+ /* instantiate i_sessionpacket interface which messagizer implements */
+ messagizer->sessionpkt = new_sessionpkt_interface(messagizer,
+ etch_msgizer_session_packet,
+ isession); /* transportmsg now owns isession */
+
+ /* copy session interface to parent */
+ messagizer->session_packet = etch_msgizer_session_packet;
+ messagizer->session_control = isession->session_control;
+ messagizer->session_notify = isession->session_notify;
+ messagizer->session_query = isession->session_query;
+
+ /* finally set session of next lower layer to our session */
+ /* fyi we must pass the implementor of transport as thisx, i.e. packetizer */
+ messagizer->transport->set_session (messagizer->transport->thisx, messagizer->sessionpkt);
+
+ /* - - - - - - - - - - - - - - -
+ * tagged data in out
+ * - - - - - - - - - - - - - - -
+ */
+ messagizer->tdi = formatfactory->new_tagdata_input(vf);
+ if (NULL == messagizer->tdi) break;
+
+ messagizer->tdo = formatfactory->new_tagdata_output(vf);
+ if (NULL == messagizer->tdo) break;
+
+ result = 0;
+
+ } while(0);
+
+
+ if (-1 == result) {
+ etch_object_destroy(vf);
+ etch_object_destroy(messagizer);
+ messagizer = NULL;
+ }
+
+ etch_object_destroy(formatfactory);
+
+ return messagizer;
+}
+
+
+/**
+ * destroy_messagizer()
+ * destructor for etch_messagizer
+ */
+int destroy_messagizer (void* data)
+{
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ if (!is_etchobj_static_content(thisx))
+ {
+ etch_object_destroy(thisx->transportmsg);
+ etch_object_destroy(thisx->sessionpkt);
+ etch_object_destroy(thisx->tdi);
+ etch_object_destroy(thisx->tdo);
+ etch_object_destroy(thisx->msgbuf);
+ etch_object_destroy(thisx->msglock);
+
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * etch_msgizer_get_transport()
+ * @return a reference to the messagizer transport interface.
+ * this object is owned by whatever object created the messagizer.
+ */
+i_transportpacket* etch_msgizer_get_transport (etch_messagizer* thisx)
+{
+ ETCH_ASSERT(is_etch_messagizer(thisx));
+ return thisx->transport;
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * i_transportmessage
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etch_msgizer_transport_message()
+ * i_transportmessage::transport_message override.
+ * serializes message and delivers its data to transport
+ * @param whoto recipient - caller retains this memory, can be null.
+ * @param message the message
+ * caller relinquishes this memory on success, retains on failure.
+ * @return 0 success, -1 error.
+ */
+int etch_msgizer_transport_message (void* data, void* whoData, void* messageData)
+{
+
+ etch_who* whoto = (etch_who*)whoData;
+ etch_message* msg = (etch_message*)messageData;
+
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ etch_status_t status = ETCH_SUCCESS;
+ int result = 0;
+ ETCH_ASSERT(is_etch_messagizer(thisx));
+
+ status = etch_mutex_lock(thisx->msglock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+
+ do
+ { const int headersize = thisx->transport->get_headersize (thisx->transport);
+
+ etch_flexbuf_skip (thisx->msgbuf, headersize, TRUE);
+
+ /* serialize the message to the buffer */
+ result = ((struct i_tagged_data_output*)
+ (((etch_object*)thisx->tdo)->vtab))
+ ->write_message (thisx->tdo, msg, thisx->msgbuf);
+
+ if (-1 == result) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR,"serialization of message to buffer failed");
+ etch_flexbuf_reset(thisx->msgbuf);
+ break;
+ }
+
+ etch_flexbuf_set_index (thisx->msgbuf, 0);
+
+
+ /* deliver packet buffer to transport */ /* msgizer->itp->pktizer */
+ result = thisx->transport->transport_packet (thisx->transport->thisx, whoto, thisx->msgbuf);
+
+ if (0 == result) {
+ etch_object_destroy(msg);
+ }
+
+ } while(0);
+
+ status = etch_mutex_unlock(thisx->msglock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+
+ return result;
+}
+
+
+/**
+ * etch_msgizer_get_session()
+ * @return a reference to the messagizer i_sessionmessage interface.
+ * this object is owned by whatever object set the messagizer session.
+ */
+i_session* etch_msgizer_get_session (void* data)
+{
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ ETCH_ASSERT(is_etch_messagizer(thisx));
+ return (i_session*)thisx->session;
+}
+
+
+/**
+ * etch_msgizer_set_session()
+ * @param session an i_sessionmessage reference. caller owns this object.
+ */
+void etch_msgizer_set_session (void* data, void* sessionData)
+{
+ i_session* session = (i_session*)sessionData;
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ ETCH_ASSERT(is_etch_messagizer(thisx));
+ ETCH_ASSERT(is_etch_sessionmsg(session));
+ thisx->session = (i_sessionmessage*)session;
+}
+
+
+/**
+ * etch_msgizer_transport_control()
+ * i_transportmessage::transport_control override.
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+int etch_msgizer_transport_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ ETCH_ASSERT(is_etch_messagizer(thisx));
+ /* mzr itp mzr itp packetizer */
+ return thisx->transport->transport_control (thisx->transport->thisx, control, value);
+}
+
+
+/**
+ * etch_msgizer_transport_notify()
+ * i_transportmessage::transport_notify override.
+ * @param evt, caller relinquishes.
+ */
+int etch_msgizer_transport_notify (void* data, etch_event* evt)
+{
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ ETCH_ASSERT(is_etch_messagizer(thisx));
+ return thisx->transport->transport_notify (thisx->transport->thisx, evt);
+}
+
+
+/**
+ * etch_msgizer_transport_query()
+ * i_transportmessage::transport_query override.
+ * @param query, caller relinquishes.
+ */
+etch_object* etch_msgizer_transport_query (void* data, etch_query* query)
+{
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ ETCH_ASSERT(is_etch_messagizer(thisx));
+ return thisx->transport->transport_query (thisx->transport->thisx, query);
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * i_sessionpacket
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etch_msgizer_session_packet()
+ * i_sessionpacket::session_packet override.
+ * delivers data to the session from the transport.
+ * @param from from who sent the packet.
+ * caller retains memory for this object, can be null.
+ * @param buffer the packet from the packet source.
+ * caller retains memory for this object.
+ * @return 0 success, -1 error (exception condition)
+ */
+int etch_msgizer_session_packet (void* data, void* whoData, void* bufferData)
+{
+
+ etch_who* whofrom = (etch_who*)whoData;
+ etch_flexbuffer* fbuf = (etch_flexbuffer*)bufferData;
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ etch_message* msg = NULL;
+ int is_message_handled = FALSE;
+ ETCH_ASSERT(is_etch_messagizer(thisx));
+
+ /* create an etch message from the packetized data */
+ msg = ((struct i_tagged_data_input*)((etch_object*)thisx->tdi)->vtab)->read_message (thisx->tdi, fbuf);
+
+ /* send the new message up via session.
+ * memory management rules are: if session_message() handles the message,
+ * it owns msg memory. otherwise (if not handled), msg memory is owned by
+ * the unwanted_message wrapper created here, which itself is owned by the
+ * session_notify() destination.
+ */
+ is_message_handled = (0 == thisx->session->session_message (thisx->session->thisx, whofrom, msg));
+
+ /* if the message was not handled, e.g. the message is an exception returned
+ * from a one-way message and there was therefore no mailbox to receive it,
+ * forward the message to the session via mailbox manager.
+ */
+ if (!is_message_handled) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "unable to post message to a mailbox\n");
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "deferring '%s' to session\n", message_aname(msg));
+ thisx->session->session_notify(thisx->session->thisx, (etch_event*)new_unwanted_message(whofrom, msg));
+ //etch_object_destroy(msg);
+ return -1;
+ }
+
+ /* regardless we have relinquished msg at this point. it is now either queued up
+ * in a mailbox, or forwarded in the unwanted message wrapper above. */
+ return 0;
+}
+
+
+/**
+ * etch_msgizer_session_control()
+ * i_sessionpacket::session_control override.
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+int etch_msgizer_session_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ ETCH_ASSERT(is_etch_messagizer(thisx)); /* ism mbmgr */
+ return thisx->session->session_control (thisx->session->thisx, control, value);
+}
+
+
+/**
+ * etch_msgizer_session_notify()
+ * i_sessionpacket::session_notify override.
+ * @param event, caller relinquishes.
+ */
+int etch_msgizer_session_notify (void* data, etch_event* evt)
+{
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ ETCH_ASSERT(is_etch_messagizer(thisx)); /* ism mbmgr */
+ return thisx->session->session_notify (thisx->session->thisx, evt);
+}
+
+
+/**
+ * etch_msgizer_session_query()
+ * i_sessionpacket::session_query override.
+ * @param query, caller relinquishes.
+ */
+etch_object* etch_msgizer_session_query (void* data, etch_query* query)
+{
+ etch_messagizer* thisx = (etch_messagizer*)data;
+ ETCH_ASSERT(is_etch_messagizer(thisx)); /* ism mbmgr */
+ return thisx->session->session_query (thisx->session->thisx, query);
+}
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_packetizer.c b/binding-c/runtime/c/src/main/transport/etch_packetizer.c
new file mode 100644
index 0000000..58ad191
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_packetizer.c
@@ -0,0 +1,520 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_packetizer.c
+ * packetizes a stream data source. Reads and verfifies a packet header consisting
+ * of a 32-bit signature and 32-bit length. verifies the flag, using length extracted
+ * from header; reads packet data and forwards it to the packet handler. as a packet
+ * source, accepts a packet and prepends a packet header prior to delivering it to a
+ * data source.
+ */
+
+#include "etch_packetizer.h"
+#include "etch_objecttypes.h"
+#include "etch_url.h"
+#include "etch_log.h"
+
+static const char* LOG_CATEGORY = "etch_packetizer";
+
+etch_packetizer* new_packetizer_b (i_transportdata*, const int maxpktsize);
+int etch_pktizer_session_control (void*, etch_event*, etch_object*);
+int etch_pktizer_session_notify (void*, etch_event*);
+etch_object* etch_pktizer_session_query (void*, etch_query*);
+int etch_pktizer_transport_control(void*, etch_event*, etch_object*);
+int etch_pktizer_transport_notify (void*, etch_event*);
+etch_object* etch_pktizer_transport_query (void*, etch_query*);
+
+const wchar_t* ETCH_PKTIZER_MAX_PKT_SIZE_TERM = L"Packetizer.maxPktSize";
+const int ETCH_PKTIZER_DEFMAXPKTSIZE = 10240;
+const int ETCH_PKTIZER_HEADERSIZE = 8;
+const int ETCH_PKTIZER_SIG = 0xdeadbeef;
+
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * packetizer constructor/destructor
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_packetizer()
+ * * etch_packetizer public constructor
+ * @param itd i_transportdata interface owned by caller
+ * @param uri a URI string owned by caller.
+ * @param resources a resources map owned by caller.
+ */
+etch_packetizer* new_packetizer (i_transportdata* itd, wchar_t* uri, etch_resources* resxmap)
+{
+ etch_url* url = new_url(uri);
+
+ etch_packetizer* packetizer = new_packetizer_a (itd, url, resxmap);
+
+ etch_object_destroy(url);
+ return packetizer;
+}
+
+
+/**
+ * new_packetizer_a()
+ * etch_packetizer constructor 2
+ */
+etch_packetizer* new_packetizer_a (i_transportdata* itd, etch_url* url, etch_resources* resxmap)
+{
+ int maxpktsize = 0, result = 0;
+
+ result = etchurl_get_integer_term (url, ETCH_PKTIZER_MAX_PKT_SIZE_TERM, &maxpktsize);
+ if (-1 == result || maxpktsize <= 0) maxpktsize = ETCH_PKTIZER_DEFMAXPKTSIZE;
+
+ return new_packetizer_b (itd, maxpktsize);
+}
+
+
+/*
+ * destroy_packetizer()
+ * etch_packetizer destructor
+ */
+int destroy_packetizer(void* data)
+{
+ etch_packetizer* thisx = (etch_packetizer*)data;
+ etch_status_t status = ETCH_SUCCESS;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ etch_object_destroy(thisx->sessiondata);
+
+ etch_object_destroy(thisx->transportpkt);
+
+ if (thisx->datalock) {
+ status = etch_mutex_destroy(thisx->datalock);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ }
+
+ etch_object_destroy(thisx->savebuf);
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
+
+
+/**
+ * etch_pktizer_transport_packet()
+ * delivers packet to transport after adding packet header.
+ * @param whoto recipient - caller retains this memory.
+ * @param fbuf buffer positioned at the packet data, with space for header
+ * @return 0 success, -1 error
+ */
+int etch_pktizer_transport_packet(void* data, void* whoData, void* bufferData)
+{
+ etch_who* whoto = (etch_who*)whoData;
+ etch_flexbuffer* fbuf = (etch_flexbuffer*)bufferData;
+ etch_packetizer* thisx = (etch_packetizer*)data;
+ int result = -1;
+ ETCH_ASSERT(is_etch_packetizer(thisx));
+
+ do
+ { size_t saveindex = 0;
+ const int datalen = (int) etch_flexbuf_avail(fbuf);
+
+ if (datalen < ETCH_PKTIZER_HEADERSIZE) break;
+
+ saveindex = fbuf->index;
+
+ etch_flexbuf_put_int(fbuf, ETCH_PKTIZER_SIG);
+ etch_flexbuf_put_int(fbuf, datalen - ETCH_PKTIZER_HEADERSIZE);
+
+ fbuf->index = saveindex;
+
+ /* deliver packet buffer to transport */
+ result = thisx->transport->transport_data(thisx->transport->thisx, whoto, fbuf);
+
+ } while(0);
+
+ return result;
+}
+
+/**
+ * etch_pktizer_session_data()
+ * i_sessiondata::session_data override.
+ * delivers data to the session from the transport
+ * @param whofrom from who sent the packet data
+ * caller retains this memory, can be null.
+ * @param fbuf the packet from the packet source positioned at the data, caller retains.
+ * @return 0 success, -1 error.
+ */
+int etch_pktizer_session_data (void* data, void* whoData, void* bufferData)
+{
+ etch_packetizer* pzr = (etch_packetizer*)data;
+ etch_who* whofrom = (etch_who*)whoData;
+ etch_flexbuffer* fbuf = (etch_flexbuffer*)bufferData;
+ /* two scenarios: the first is that we have no buffered data and the
+ * entire packet is contained within the buffer. in that case we can skip
+ * the details and ship the packet directly to the handler.
+ */
+ size_t curlen, curndx, bytes_avail, fbuf_avail, packet_bodylen, bytes_put;
+ int bytes_needed;
+ etch_flexbuffer* savebuf = NULL;
+ ETCH_ASSERT(is_etch_packetizer(pzr));
+ savebuf = pzr->savebuf;
+
+ while(1) /* while(fbuf.avail() > 0 ... */
+ { if (0 >= (fbuf_avail = etch_flexbuf_avail(fbuf))) break;
+
+ bytes_avail = savebuf->datalen + fbuf_avail;
+
+ if (pzr->is_wantheader)
+ {
+ if (bytes_avail >= (unsigned) ETCH_PKTIZER_HEADERSIZE)
+ { /* there are enough bytes in the pipeline for a header */
+ if (savebuf->datalen == 0)
+ { /* save buffer is empty, entire header in fbuf */
+ packet_bodylen = etch_pktizer_process_header(pzr, fbuf, FALSE);
+ }
+ else /* save non empty, header split across save and buf, so ... */
+ { /* move enough bytes from buf to save to complete a header */
+ bytes_needed = ETCH_PKTIZER_HEADERSIZE - (int) savebuf->datalen;
+
+ bytes_put = etch_flexbuf_put_from (savebuf, fbuf, bytes_needed);
+ if (0 == bytes_put) return -1;
+ etch_flexbuf_set_index(savebuf, 0);
+ packet_bodylen = etch_pktizer_process_header(pzr, savebuf, TRUE);
+ }
+
+ if (packet_bodylen == -1) return -1; /* bad header */
+ if (packet_bodylen == 0) continue; /* header with empty body */
+ pzr->bodylength = packet_bodylen;
+ pzr->is_wantheader = FALSE;
+ }
+ else /* want header, but too few bytes in the pipeline */
+ { /* ... so save fbuf to the save buffer */
+ etch_flexbuf_set_index (savebuf, savebuf->datalen);
+ etch_flexbuf_put_from(savebuf, fbuf, -1);
+ }
+ }
+ else /* didn't need a header, so ... */
+ if (bytes_avail >= pzr->bodylength)
+ {
+ /* we need the body, and there are enough bytes in the pipeline.
+ * three scenarios: the body is entirely in savebuf, the body is
+ * split, or the body is entirely in fbuf. assert that the body
+ * is not entirely in save, or we'd have processed it last time.
+ */
+ ETCH_ASSERT(savebuf->datalen < pzr->bodylength);
+
+ if (savebuf->datalen == 0)
+ { /* saved buffer is empty, entire body is in fbuf */
+ curlen = fbuf->datalen;
+ curndx = fbuf->index;
+ etch_flexbuf_set_length(fbuf, curndx + pzr->bodylength);
+
+ /* send the packet in the input buffer up the chain to the next
+ * higher layer of the transport stack. fyi packetizer.session.thisx
+ * is that layer object, ordinarily the messagizer.
+ */
+ pzr->session->session_packet (pzr->session->thisx, whofrom, fbuf);
+
+ /* fyi the input buffer can contain a partial packet, multiple
+ * packets, or whatever, so we may not be done with it yet. */
+ etch_flexbuf_set_length(fbuf, curlen);
+ etch_flexbuf_set_index (fbuf, curndx + pzr->bodylength);
+ pzr->is_wantheader = TRUE;
+ }
+ else
+ { /* savebuf.datalen not zero, so body is split across the save buffer
+ * and the input buffer. move enough bytes from input buffer fbuf
+ * to complete the packet body.
+ */
+ bytes_needed = (int) (pzr->bodylength - savebuf->datalen);
+ bytes_put = etch_flexbuf_put_from (savebuf, fbuf, bytes_needed);
+ if (bytes_put <= 0) return -1;
+ etch_flexbuf_set_index(savebuf, 0);
+
+ /* send the newly-isolated packet up the chain to the next higher
+ * layer of the transport stack. fyi packetizer.session.thisx
+ * is that layer object, ordinarily the messagizer.
+ */
+ pzr->session->session_packet (pzr->session->thisx, whofrom, savebuf);
+
+ etch_flexbuf_reset(savebuf);
+ pzr->is_wantheader = TRUE;
+ }
+ }
+ else /* need body but too few bytes in pipeline to complete it */
+ { /* ... so save the input buffer to the save buffer */
+ bytes_put = etch_flexbuf_put_from (savebuf, fbuf, ETCH_FLEXBUF_PUT_ALL);
+ }
+ }
+
+ /* buffer is now empty and we're done */
+ return fbuf_avail == 0? 0: -1;
+}
+
+
+/**
+ * etch_pktizer_set_session()
+ * set packetizer session interface presumably to that of the next higher layer.
+ * @param session an i_sessionpacket reference. caller owns this object.
+ */
+void etch_pktizer_set_session (void* data, void* sessionData)
+{
+ etch_packetizer* thisx = (etch_packetizer*)data;
+ i_session* newsession = (i_session*)sessionData;
+ ETCH_ASSERT(is_etch_packetizer(thisx));
+ ETCH_ASSERT(is_etch_sessionpacket(newsession));
+ thisx->session = (i_sessionpacket*)newsession;
+}
+
+
+/**
+ * new_packetizer_b()
+ * etch_packetizer private constructor
+ * @param transport the transport interface of the next lower layer of the stack,
+ * that being the connection, e.g. etch_tcp_connection. not owned.
+ * @param maxpktsize maximum number of bytes in a packet (default currently 10240)
+ */
+etch_packetizer* new_packetizer_b (i_transportdata* itd, const int maxpktsize)
+{
+ etch_packetizer* packetizer = NULL;
+ i_transport* itransport = NULL;
+ i_session* isession = NULL;
+ etch_mutex* mutex = NULL;
+ int result = -1;
+ ETCH_ASSERT(is_etch_transportdata(itd));
+
+ do
+ {
+ #if(ETCHPZR_HAS_MUTEX)
+ if (NULL == (mutex = new_mutex(etch_apr_mempool, ETCHMUTEX_NESTED))) break;
+ #endif
+
+ /* - - - - - - - - - - - - - - -
+ * etch_packetizer
+ * - - - - - - - - - - - - - - -
+ */
+ packetizer = (etch_packetizer*) new_object
+ (sizeof(etch_packetizer), ETCHTYPEB_PACKETIZER, CLASSID_PACKETIZER);
+
+ ((etch_object*)packetizer)->destroy = destroy_packetizer;
+ ((etch_object*)packetizer)->clone = clone_null;
+ packetizer->datalock = mutex;
+
+ packetizer->headersize = ETCH_PKTIZER_HEADERSIZE;
+ packetizer->is_wantheader = TRUE;
+ packetizer->maxpacketsize = maxpktsize > 0? maxpktsize: ETCH_PKTIZER_DEFMAXPKTSIZE;
+
+ packetizer->savebuf = new_flexbuffer(ETCH_DEFSIZE); /* 2K default */
+
+ /* set our transport to that of next lower layer (connection) */
+ packetizer->transport = itd; /* not owned */
+
+
+ /* - - - - - - - - - - - - - - -
+ * i_transportpacket
+ * - - - - - - - - - - - - - - -
+ */
+ itransport = new_transport_interface_ex (packetizer,
+ (etch_transport_control) etch_pktizer_transport_control,
+ (etch_transport_notify) etch_pktizer_transport_notify,
+ (etch_transport_query) etch_pktizer_transport_query,
+ etch_pktizer_get_session,
+ etch_pktizer_set_session);
+
+ /* instantiate i_transportpacket interface which packetizer implements */
+ packetizer->transportpkt = new_transportpkt_interface (packetizer,
+ etch_pktizer_transport_packet,
+ itransport); /* transportpkt now owns itransport */
+
+ /* copy i_transportpacket interface methods up to packetizer */
+ packetizer->transport_packet = etch_pktizer_transport_packet;
+ packetizer->transport_control = itransport->transport_control;
+ packetizer->transport_notify = itransport->transport_notify;
+ packetizer->transport_query = itransport->transport_query;
+ packetizer->get_session = itransport->get_session;
+ packetizer->set_session = itransport->set_session;
+
+
+ /* - - - - - - - - - - - - - - -
+ * i_sessiondata
+ * - - - - - - - - - - - - - - -
+ */
+ isession = new_session_interface (packetizer,
+ (etch_session_control) etch_pktizer_session_control,
+ (etch_session_notify) etch_pktizer_session_notify,
+ (etch_session_query) etch_pktizer_session_query);
+
+ /* instantiate i_sessiondata interface which packetizer implements */
+ packetizer->sessiondata = new_sessiondata_interface(packetizer,
+ etch_pktizer_session_data,
+ isession); /* sessiondata now owns isession */
+
+ /* copy session interface to parent */
+ packetizer->session_data = etch_pktizer_session_data;
+ packetizer->session_control = isession->session_control;
+ packetizer->session_notify = isession->session_notify;
+ packetizer->session_query = isession->session_query;
+
+ /* finally set session of next lower layer (tcp connection) to our session */
+ /* fyi we must pass the implementor of transport as thisx, i.e. tcpconnection */
+ packetizer->transport->set_session (packetizer->transport->thisx, packetizer->sessiondata);
+
+ result = 0;
+
+ } while(0);
+
+
+ if (-1 == result) {
+ etch_object_destroy(packetizer);
+ packetizer = NULL;
+ }
+
+ return packetizer;
+}
+
+
+
+
+/* - - - - - - - - - - - - - - -
+ * i_transportpacket
+ * - - - - - - - - - - - - - - -
+ */
+
+
+/**
+ * etch_pktizer_get_session()
+ * @return a reference to the packetizer i_sessiondata interface.
+ * this object is owned by whatever object set the packetizer session.
+ */
+i_session* etch_pktizer_get_session (void* data)
+{
+ etch_packetizer* thisx = (etch_packetizer*)data;
+ ETCH_ASSERT(is_etch_packetizer(thisx));
+ return (i_session*)thisx->session;
+}
+
+
+
+
+/**
+ * etch_pktizer_transport_control()
+ * i_transportpacket::transport_control override.
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+int etch_pktizer_transport_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_packetizer* thisx = (etch_packetizer*)data;
+ ETCH_ASSERT(is_etch_packetizer(thisx));
+ /* pzr itd pzr itd tcpconx */
+ return thisx->transport->transport_control (thisx->transport->thisx, control, value);
+}
+
+
+/**
+ * etch_pktizer_transport_notify()
+ * i_transportpacket::transport_notify override.
+ * @param evt, caller relinquishes.
+ */
+int etch_pktizer_transport_notify (void* data, etch_event* evt)
+{
+ etch_packetizer* thisx = (etch_packetizer*)data;
+ ETCH_ASSERT(is_etch_packetizer(thisx));
+ return thisx->transport->transport_notify (thisx->transport->thisx, evt);
+}
+
+
+/**
+ * etch_pktizer_transport_query()
+ * i_transportpacket::transport_query override.
+ * @param query, caller relinquishes.
+ */
+etch_object* etch_pktizer_transport_query (void* data, etch_query* query)
+{
+ etch_packetizer* thisx = (etch_packetizer*)data;
+ ETCH_ASSERT(is_etch_packetizer(thisx));
+ return thisx->transport->transport_query (thisx->transport->thisx, query);
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * i_sessiondata
+ * - - - - - - - - - - - - - - -
+ */
+
+/*
+ * etch_pktizer_process_header()
+ * extract header information from packet
+ * returns data length from header, or -1 if bad header
+ */
+int etch_pktizer_process_header (etch_packetizer* pzr, etch_flexbuffer* fbuf, const int is_reset)
+{
+ int curlen = 0, signature = 0;
+ int result = etch_flexbuf_get_int(fbuf, &signature);
+ if (-1 == result || ETCH_PKTIZER_SIG != signature) return -1;
+
+ result = etch_flexbuf_get_int(fbuf, &curlen);
+
+ if (is_reset)
+ etch_flexbuf_reset(fbuf);
+
+ if( curlen > (int) pzr->maxpacketsize) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_WARN, "Packetsize in header is too long, dropping header.");
+ }
+
+ return result == -1 || curlen < 0 || curlen > (int) pzr->maxpacketsize?
+ -1: curlen;
+}
+
+
+/**
+ * etch_pktizer_session_control()
+ * i_sessiondata::session_control override.
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+int etch_pktizer_session_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_packetizer* thisx = (etch_packetizer*)data;
+ ETCH_ASSERT(is_etch_packetizer(thisx));
+ return thisx->session->session_control (thisx->session->thisx, control, value);
+}
+
+
+/**
+ * etch_pktizer_session_notify()
+ * i_sessiondata::session_notify override.
+ * @param event, caller relinquishes.
+ */
+int etch_pktizer_session_notify (void* data, etch_event* evt)
+{
+ etch_packetizer* thisx = (etch_packetizer*)data;
+ ETCH_ASSERT(is_etch_packetizer(thisx));
+ return thisx->session->session_notify (thisx->session->thisx, evt);
+}
+
+
+/**
+ * etch_pktizer_session_query()
+ * i_sessiondata::session_query override.
+ * @param query, caller relinquishes.
+ */
+etch_object* etch_pktizer_session_query (void* data, etch_query* query)
+{
+ etch_packetizer* thisx = (etch_packetizer*)data;
+ ETCH_ASSERT(is_etch_packetizer(thisx));
+ return thisx->session->session_query (thisx->session->thisx, query);
+}
diff --git a/binding-c/runtime/c/src/main/transport/etch_plain_mailbox.c b/binding-c/runtime/c/src/main/transport/etch_plain_mailbox.c
new file mode 100644
index 0000000..2f8bdc8
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_plain_mailbox.c
@@ -0,0 +1,927 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_plainmailbox.c
+ * standard mailbox using a fixed size queue
+ */
+
+#include "etch_runtime.h"
+#include "etch_plain_mailbox.h"
+#include "etch_encoding.h"
+#include "etch_thread.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+static const char* LOG_CATEGORY = "etch_plain_mailbox";
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+etch_plainmailbox* new_mailbox_a(const int);
+int destroy_mailbox(void*);
+void etchmbox_alarm_wakeup (void*, const int);
+int etchmbox_is_alarm_set(etch_plainmailbox*);
+int etchmbox_set_duration (etch_plainmailbox*, const int);
+int etchmbox_test_and_set_alarm_set(etch_plainmailbox*, const int);
+
+int etchmbox_message(void*, etch_who* whofrom, etch_message* msg);
+int etchmbox_read (void*, etch_mailbox_element** out);
+int etchmbox_read_withwait (void*, const int maxdelay, etch_mailbox_element** out);
+int etchmbox_close_delivery (void*);
+int etchmbox_close_read (void* );
+int etchmbox_register_notify (void*, etch_mailbox_notify, etch_object* state, const int maxdelay);
+int etchmbox_unregister_notify (void*, etch_mailbox_notify);
+int etchmbox_is_empty (void*);
+int etchmbox_is_closed (void*);
+int etchmbox_is_full (void*);
+int etchmbox_fire_notify(void*);
+int64 etchmbox_get_message_id (void*);
+
+
+/* - - - - - - - - - - - - - - -
+ * constructor/destructor
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_mailbox()
+ * etch_plainmailbox public constructor
+ * @param mbmgr the manager to use to unregister this mailbox
+ * and to forward undelivered messages.
+ * @param message_id
+ * @param max_msgdelay the maximum delay in milliseconds to wait when attempting
+ * to put a message to the mailbox.
+ * @param lifetime time in milliseconds to keep the mailbox open.
+ * a lifetime of zero means keep it open until explicitly closed.
+ * @param capacity maximum number of messages. zero indicates use default capacity.
+ */
+etch_plainmailbox* new_mailbox (i_mailbox_manager* mbmgr, const int64 message_id,
+ const int max_msgdelay, const int lifetime, const int capacity)
+{
+ etch_plainmailbox* mbox = NULL;
+ if (message_id == 0 || lifetime < 0 || capacity < 0) return NULL;
+
+ if (NULL == (mbox = new_mailbox_a(capacity))) return NULL;
+
+ mbox->manager = mbmgr;
+ mbox->lifetime = lifetime;
+ mbox->rwlock = mbmgr->rwlock; /* not owned */
+ mbox->message_id = message_id;
+ mbox->max_message_delay = max_msgdelay;
+ mbox->max_messages = capacity > 0? capacity: MBOX_DEFMAXMESSAGES;
+
+ if (lifetime)
+ etchmbox_set_duration(mbox, lifetime);
+
+ return mbox;
+}
+
+
+/**
+ * new_mailbox_a()
+ * etch_plainmailbox private constructor
+ */
+etch_plainmailbox* new_mailbox_a(const int capacity)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_plainmailbox* mailbox = NULL;
+ i_mailbox* imailbox = NULL;
+ etch_queue* queue = NULL;
+ etch_mutex_t* mutex = NULL;
+ int result = -1;
+
+ do
+ {
+ // TODO: pool
+ status = etch_mutex_create(&mutex, ETCH_MUTEX_UNNESTED, NULL);
+ if(status != ETCH_SUCCESS) {
+ // error
+ break;
+ }
+ if (NULL == (queue = new_queue(capacity))) break;
+
+ /* - - - - - - - - - - - - - - -
+ * i_mailbox
+ * - - - - - - - - - - - - - - -
+ */
+ imailbox = new_mailbox_interface( NULL, /* parent set below */
+ etchmbox_message,
+ etchmbox_read,
+ etchmbox_read_withwait,
+ etchmbox_close_delivery,
+ etchmbox_close_read,
+ etchmbox_register_notify,
+ etchmbox_unregister_notify,
+ etchmbox_is_empty,
+ etchmbox_is_closed,
+ etchmbox_is_full,
+ etchmbox_get_message_id);
+
+ /* - - - - - - - - - - - - - - -
+ * etch_plainmailbox
+ * - - - - - - - - - - - - - - -
+ */
+ mailbox = (etch_plainmailbox*) new_object(sizeof(etch_plainmailbox), ETCHTYPEB_MAILBOX, CLASSID_MAILBOX);
+
+ ((etch_object*)mailbox)->destroy = destroy_mailbox;
+ ((etch_object*)mailbox)->clone = clone_null;
+ mailbox->readlock = mutex;
+ mailbox->queue = queue;
+
+ /* copy i_mailbox virtuals to parent */
+ imailbox->thisx = mailbox;
+ mailbox->imailbox = imailbox;
+ mailbox->message = etchmbox_message;
+ mailbox->read = etchmbox_read;
+ mailbox->read_withwait = etchmbox_read_withwait;
+ mailbox->close_delivery = etchmbox_close_delivery;
+ mailbox->close_read = etchmbox_close_read;
+ mailbox->register_notify = etchmbox_register_notify;
+ mailbox->unregister_notify = etchmbox_unregister_notify;
+ mailbox->is_empty = etchmbox_is_empty;
+ mailbox->is_closed = etchmbox_is_closed;
+ mailbox->is_full = etchmbox_is_full;
+ mailbox->get_message_id = etchmbox_get_message_id;
+
+ mailbox->mailbox_state = ETCH_MAILBOX_STATE_OPEN;
+ result = 0;
+
+ } while(0);
+
+ if (-1 == result)
+ {
+ etch_mutex_destroy(mutex);
+ etch_object_destroy(queue);
+ if (imailbox)
+ etch_free(imailbox);
+ }
+
+ return mailbox;
+}
+
+
+/**
+ * destroy_mailbox()
+ * destructor for etch_plainmailbox (etch_mailbox)
+ */
+int destroy_mailbox(void* data)
+{
+ etch_plainmailbox* thisx = (etch_plainmailbox*)data;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ const int is_locked = 0 == etchqueue_lock(thisx->queue);
+ const int current_state = thisx->mailbox_state;
+ thisx->mailbox_state = ETCH_MAILBOX_STATE_SHUTDOWN;
+ if (is_locked) etchqueue_unlock(thisx->queue);
+
+ if (current_state < ETCH_MAILBOX_STATE_CLOSED_READ)
+ { /* if the mailbox has not been closed, do it now. we do this
+ * in order to close the queue, cancel any active timer, and
+ * reroute any messages remaining in the mailbox. */
+ etchmbox_close_read(thisx->imailbox);
+ etch_sleep(100); /* todo: remove sleep when tested out */
+ }
+
+ if (thisx->impl)
+ ((etch_object*) thisx->impl)->destroy(thisx->impl);
+
+
+ etch_object_destroy(thisx->queue);
+ thisx->queue = NULL;
+
+
+ etch_object_destroy(thisx->notify_state);
+ thisx->notify_state = NULL;
+
+
+ etch_object_destroy(thisx->lifetimer);
+ thisx->lifetimer = NULL;
+
+
+ etch_mutex_destroy(thisx->readlock);
+ thisx->readlock = NULL;
+
+ /* debug heap issue note: this is/was the spot */
+ etch_free(thisx->imailbox);
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * etch_plainmailbox public
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchmbox_contains_message()
+ * determines if the mailbox contains a message with the same memory address
+ * as the specified message.
+ * @return TRUE or FALSE.
+ */
+int etchmbox_contains_message(etch_plainmailbox* thisx, etch_message* thismsg)
+{
+ int result = FALSE, entrycount = 0;
+
+ if (0 == etchqueue_lock(thisx->queue))
+ {
+ entrycount = thisx && thisx->queue? etch_apr_queue_size(thisx->queue->aprq): 0;
+
+ if (entrycount)
+ {
+ etch_iterator iterator;
+ set_iterator(&iterator, thisx->queue, &thisx->queue->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ etch_mailbox_element* content = (etch_mailbox_element*) iterator.current_value;
+
+ if (content && content->msg && content->msg == thismsg)
+ { result = TRUE;
+ break;
+ }
+
+ iterator.next(&iterator);
+ }
+ }
+
+ etchqueue_unlock(thisx->queue);
+ }
+
+ return result;
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * alarm interface
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchmbox_alarm_wakeup()
+ * callback from alarm timer indicating timer expired or signaled.
+ */
+void etchmbox_alarm_wakeup (void* passthrudata, const int wakeupreason)
+{
+ etch_plainmailbox* thisx = (etch_plainmailbox*) passthrudata;
+
+ ETCH_ASSERT(is_etch_mailbox(thisx));
+ /* note that the timer can not be destroyed here - its thread has not exited */
+
+ /* if we are called back in the middle of closing the mailbox we will block here
+ * until the close is complete. the close_delivery() below will then do nothing.
+ * related scenario: another thread's call to close_delivery() is blocked on this
+ * etchmbox_test_and_set_alarm_set. etchmbox_test_and_set_alarm_set releases the
+ * lock, the other close_delivery() acquires the lock and proceeds. meanwhile,
+ * close_delivery() called here (3 lines below), blocks, since the other thread
+ * has the the lock. when the the other thread completes close_delivery(), the
+ * lock is released and this close_delivery() proeceeds to find that the queue
+ * is now closed, so it takes no action and returns.
+ */
+
+ #ifdef ETCH_DEBUG
+ ETCH_LOG(ETCH_PLAINMAILBOX_LOGID, ETCH_LOG_XDEBUG, "wakeup reason %s\n", reasons[wakeupreason+1]);
+ #endif
+
+ if (wakeupreason == ETCH_TIMEOUT)
+ thisx->close_delivery(thisx->imailbox);
+}
+
+
+/**
+ * etchmbox_set_duration()
+ */
+int etchmbox_set_duration(etch_plainmailbox* thisx, const int ms)
+{
+ thisx->is_alarm_set = TRUE; /* todo implement proper alarm manager */
+ thisx->lifetimer = new_timer(ms, thisx, etchmbox_alarm_wakeup);
+
+ if (thisx->lifetimer)
+ thisx->lifetimer->start(thisx->lifetimer);
+
+ return NULL != thisx->lifetimer;
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * i_mailbox
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchmbox_message()
+ * queues specified message to this mailbox.
+ * @param whofrom caller retains
+ * @param msg caller relinquishes on success, retains on other than success.
+ * message is not destroyed here on failure case since caller may want to reroute.
+ * @return 0 sucesss, -1 failure, ETCH_MAILBOX_TIMEOUT (-2) timeout,
+ * or ETCH_MAILBOX_DUPLICATE (-3), message already queued.
+ */
+int etchmbox_message (void* data, etch_who* whofrom, etch_message* msg)
+{
+ i_mailbox* ibox = (i_mailbox*)data;
+ int result = 0;
+ etch_mailbox_element* msgelt;
+ const char* thistext = "etchmbox_message";
+ etch_plainmailbox* thisx = ibox? ibox->thisx: NULL;
+ if (NULL == thisx) return -1;
+
+ if (etchmbox_contains_message(thisx, msg))
+ return ETCH_MAILBOX_DUPLICATE;
+
+ msgelt = new_mailbox_element(msg, whofrom);
+
+ /* block the mailbox from being read until the put returns and the status
+ * change notification completes. without this lock, the message could be read
+ * from the mailbox, causing all client processing to complete, resulting in
+ * destruction of the mailbox before the put returns, resulting in a race
+ * condition when the notify references a mailbox which has now been destroyed.
+ * the lock is released below prior to return from this method.
+ */
+ etchmbox_get_readlock (thisx, thistext);
+
+ /* insert message to mailbox - relinquishes message on success */
+ result = etchqueue_put_withwait (thisx->queue, thisx->max_message_delay, msgelt);
+
+ switch(result)
+ {
+ case 0: break;
+
+ case ETCH_QUEUE_OPERATION_TIMEOUT:
+ result = ETCH_MAILBOX_TIMEOUT;
+ break;
+
+ case ETCH_QUEUE_OPERATION_CANCELED: /* wait signaled */
+ default:
+ result = -1;
+ }
+
+ if (0 == result)
+ etchmbox_fire_notify ((etch_plainmailbox*)thisx);
+ else /* on failure case, we destroy the message element wrapper here,
+ * but leave the message intact, since caller may want to reroute.
+ * if we were to instead invoke the msgelt destructor, the message
+ * would also be destroyed, so we simply etch_free the wrapper. */
+ etch_free(msgelt);
+
+ etchmbox_release_readlock (thisx, thistext);
+ return result;
+}
+
+
+/**
+ * etchmbox_check_read_result()
+ * check result of mailbox read, ensure presence of out reference,
+ * and throw exception into the out object if indicated.
+ * @param readresult result of the mailbox queue read operation in question.
+ * @param out pointer to location currently hosting mailbox element read
+ * from queue, or which will receive such an object to be instantiated here.
+ * @return the passed readresult, assumed to be one of these five values:
+ * 0: read result was OK and present in the *out mailbox element.
+ * -1: some error occurred reading the queue, and *out is an empty mailbox
+ * element hosting an exception so indicating.
+ * ETCH_QUEUE_OPERATION_TIMEOUT: the queue read timed out, and *out is an empty
+ * mailbox element hosting an exception so indicating.
+ * ETCH_QUEUE_OPERATION_CANCELED: the queue read was programmatically interrupted,
+ * and *out is an empty mailbox element hosting an exception so indicating.
+ * ETCH_QUEUE_EOF: queue was empty, and *out is now null.
+ */
+int etchmbox_check_read_result (const int readresult, etch_mailbox_element** out)
+{
+ ETCH_ASSERT(out);
+
+ switch(readresult)
+ { case 0: /* normal result, verify that a return object was created */
+ ETCH_ASSERT(*out);
+ break;
+
+ case ETCH_QUEUE_EOF: /* mailbox was empty, ensure no return object */
+ *out = NULL;
+ break;
+
+ default: /* error result, create return object to wrap exception */
+ {
+ char* txt = NULL;
+ etch_exception* ex = NULL;
+ /* determine exception text */
+ switch(readresult) {
+ case ETCH_QUEUE_OPERATION_TIMEOUT:
+ txt = "mailbox read timed out";
+ break;
+ case ETCH_QUEUE_OPERATION_CANCELED:
+ txt = "mailbox read canceled";
+ break;
+ default:
+ txt = "mailbox read error";
+ break;
+ }
+
+ *out = (etch_mailbox_element*) new_etch_exception_from_errorcode(ETCH_ETIMEOUT);
+ ex = (etch_exception*)*out;
+ etch_exception_set_message(ex, new_stringa(txt));
+ clear_etchobj_static_content(((etch_object*)ex));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "%s\n", txt);
+ }
+ }
+
+ return readresult;
+}
+
+
+/**
+ * etchmbox_read()
+ * get an entry from mailbox, block until available.
+ * @param out pointer to caller's location to receive the mailbox entry.
+ * @return 0, ETCH_QUEUE_EOF, ETCH_QUEUE_OPERATION_TIMEOUT, ETCH_QUEUE_OPERATION_CANCELED, or -1.
+ * if result is 0, return via the out parameter, the message read from the mailbox, wrapped in an
+ * etch_mailbox_element object. if result is ETCH_QUEUE_EOF, mailbox was empty, and the out
+ * parameter will be null. any other result, and a placeholder etch_mailbox_element is returned
+ * via the out parameter, containing a null message, and wrapping an exception indicating the
+ * reason for mailbox read failure.
+ */
+int etchmbox_read (void* data, etch_mailbox_element** out)
+{
+ i_mailbox* ibox = (i_mailbox*)data;
+ int result = 0;
+ etch_plainmailbox* thisx = ibox? ibox->thisx: NULL;
+ ETCH_ASSERT(thisx && out);
+ *out = NULL;
+
+ result = etchqueue_get (thisx->queue, (void**)out);
+
+ return etchmbox_check_read_result (result, out);
+}
+
+
+/**
+ * etchmbox_read_withwait()
+ * @param maxdelay time interval to wait for a message to arrive in the queue.
+ * this will be one of:
+ * a) ETCH_NOWAIT, to specify no wait;
+ * b) ETCHQUEUE_CLEARING_CLOSED_QUEUE, to ask for read from a closed queue
+ * without blocking; or
+ * c) wait time in milliseconds.
+ * @param out pointer to caller's location to receive the mailbox entry.
+ * @return 0, ETCH_QUEUE_EOF, ETCH_QUEUE_OPERATION_TIMEOUT, ETCH_QUEUE_OPERATION_CANCELED, or -1.
+ * if result is 0, return via the out parameter, the message read from the mailbox, wrapped in an
+ * etch_mailbox_element object. if result is ETCH_QUEUE_EOF, mailbox was empty, and the out
+ * parameter will be null. any other result, and a placeholder etch_mailbox_element is returned
+ * via the out parameter, containing a null message, and wrapping an exception indicating the
+ * reason for mailbox read failure.
+ */
+int etchmbox_read_withwait (void* data, const int maxdelay,
+ etch_mailbox_element** out)
+{
+ i_mailbox* ibox = (i_mailbox*)data;
+ int result = 0;
+ etch_plainmailbox* thisx = ibox? ibox->thisx: NULL;
+ ETCH_ASSERT(thisx && out);
+ *out = NULL;
+
+ result = etchqueue_get_withwait (thisx->queue, maxdelay, (void**)out);
+
+ return etchmbox_check_read_result (result, out);
+}
+
+
+/**
+ * etchmbox_get_readlock()
+ * acquire the mailbox read mutex.
+ * we wrap the lock thusly to provide logging to aid in deadlock debugging.
+ * @return 0 success, -1 failure
+ */
+int etchmbox_get_readlock (etch_plainmailbox* thisx, const char* caller)
+{
+ return etchmbox_get_readlockex (thisx->rwlock, caller);
+}
+
+
+/**
+ * etchmbox_release_readlock()
+ * release the mailbox read mutex.
+ * we wrap the lock thusly to provide logging to aid in deadlock debugging.
+ * @return 0 success, -1 failure
+ */
+int etchmbox_release_readlock (etch_plainmailbox* thisx, const char* caller)
+{
+ return etchmbox_release_readlockex (thisx->rwlock, caller);
+}
+
+
+/**
+ * etchmbox_get_readlockex()
+ * acquire the specified read mutex.
+ * we wrap the lock thusly to provide logging to aid in deadlock debugging.
+ * @return 0 success, -1 failure
+ */
+int etchmbox_get_readlockex (etch_mutex* mutex, const char* caller)
+{
+ etch_status_t status = ETCH_SUCCESS;
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "%s gets mbox lock\n", caller);
+
+ status = etch_mutex_lock(mutex);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+ * etchmbox_release_readlock()
+ * release the mailbox read mutex.
+ * we wrap the lock thusly to provide logging to aid in deadlock debugging.
+ * @return 0 success, -1 failure
+ */
+int etchmbox_release_readlockex (etch_mutex* mutex, const char* caller)
+{
+ etch_status_t status = ETCH_SUCCESS;
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "%s rels mbox lock\n", caller);
+
+ status = etch_mutex_unlock(mutex);
+ if(status != ETCH_SUCCESS) {
+ return -1;
+ }
+ return 0;
+}
+
+
+
+/**
+ * etchmbox_close_delivery()
+ * @return 0 success, -1 failure
+ */
+int etchmbox_close_delivery (void* data)
+{
+ i_mailbox* ibox = (i_mailbox*)data;
+ int result = -1;
+ etch_plainmailbox* thisx = NULL;
+ ETCH_ASSERT(is_etch_imailbox(ibox));
+ thisx = ibox->thisx;
+ ETCH_ASSERT(is_etch_mailbox(thisx));
+
+ /* when arriving here via mailbox destructor, mailbox state will be
+ * ETCH_MAILBOX_STATE_SHUTDOWN but queue may not yet be closed */
+ if (thisx->mailbox_state >= ETCH_MAILBOX_STATE_CLOSED_DELIVERY
+ && etchqueue_is_closed(thisx->queue)) /* 12/24 */
+ return ETCH_MAILBOX_RESULT_ALREADY_CLOSED;
+
+ if (0 == etchqueue_lock(thisx->queue))
+ {
+ if (!etchqueue_is_closed(thisx->queue))
+ {
+ if (thisx->mailbox_state < ETCH_MAILBOX_STATE_CLOSED_DELIVERY)
+ thisx->mailbox_state = ETCH_MAILBOX_STATE_CLOSED_DELIVERY;
+
+ if (thisx->is_alarm_set)
+ {
+ thisx->is_alarm_set = FALSE;
+
+ /* this signal will usually trigger a callback to the timer
+ * expiration handler, which will block until we release this
+ * lock, and then do nothing, since the reason code will indicate
+ * signaled rather than timer expiration. however if the timer
+ * were to have expired while we are here but before we signal it,
+ * the handler would again block, we would signal it below which
+ * would do nothing since the timer had already fired, and when
+ * we finally release the lock here, the handler would continue,
+ * attempting to close the mailbox, which it would now find to be
+ * already closed since we just closed it here. if on the other
+ * hand the handler got the lock before we did, then it would
+ * close the mailbox, and we would find it closed instead.
+ */
+ etch_timer_stop(thisx->lifetimer);
+ }
+
+ /* todo when shutting down, ensure that the mailbox manager is not
+ * destroyed prior to the mailboxes, and ensure that the manager can
+ * handle an unregister call on the mailbox and that the manager does
+ * not destroy the mailbox on unregister.
+ */
+ if (thisx->manager)
+ thisx->manager->unregister (thisx->manager, thisx->imailbox);
+
+ etchqueue_close(thisx->queue, ETCHQUEUE_NOLOCK);
+ result = 0;
+ }
+
+ etchqueue_unlock(thisx->queue);
+ }
+
+ if (0 == result)
+ etchmbox_fire_notify((etch_plainmailbox*)thisx);
+
+ return result;
+}
+
+
+/**
+ * etchmbox_close_read()
+ */
+int etchmbox_close_read (void* data)
+{
+ i_mailbox* ibox = (i_mailbox*)data;
+ etch_config_t* config = NULL;
+ int32 propvalue = 0;
+ etch_mailbox_element* qitem = NULL;
+ int is_destroyentry = 0;
+ etch_int64* msgidobj = NULL;
+ int64 msgid = 0;
+ const char* delmsg = "destroyed mailbox message %d\n";
+ const char* rrerrmsg = "could not reroute message %d from closed mailbox\n";
+ etch_plainmailbox* thisx = NULL;
+
+ etch_runtime_get_config(&config);
+ ETCH_ASSERT(config);
+
+ if(ibox != NULL) {
+ thisx = ibox->thisx;
+ }
+ if(thisx == NULL) {
+ // error
+ return -1;
+ }
+
+ if (0 > etchmbox_close_delivery(ibox)) return -1;
+
+ /* state is ETCH_MAILBOX_CLOSED_DELIVERY or ETCH_MAILBOX_STATE_SHUTDOWN */
+ if (thisx->mailbox_state < ETCH_MAILBOX_STATE_CLOSED_READ)
+ thisx->mailbox_state = ETCH_MAILBOX_STATE_CLOSED_READ;
+
+ while(1) /* pop entries off queue and reroute or destroy */
+ {
+ if (0 != etchmbox_read_withwait(ibox, ETCHQUEUE_CLEARING_CLOSED_QUEUE, &qitem))
+ break;
+
+ msgidobj = qitem->msg? message_get_id(qitem->msg): NULL;
+ msgid = msgidobj? msgidobj->value: 0;
+ is_destroyentry = FALSE;
+
+ etch_config_get_property_int(config, "etch.mailbox.destroy.message", &propvalue);
+ if (propvalue == 1 && thisx->mailbox_state == ETCH_MAILBOX_STATE_SHUTDOWN)
+ is_destroyentry = TRUE;
+ else /* try to reroute the message */
+ if (0 != thisx->manager->redeliver(thisx->manager, qitem->whofrom, qitem->msg))
+ {
+ is_destroyentry = TRUE;
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, rrerrmsg, msgid);
+ }
+
+ if (is_destroyentry) /* if message was not rerouted destroy it */
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, delmsg, msgid);
+ etch_object_destroy(qitem->msg);
+ qitem->msg = NULL;
+ }
+
+ qitem->msg = NULL; /* message has been disposed in some manner */
+ etch_object_destroy(qitem); /* destroy popped element wrapper */
+ }
+
+ return 0;
+}
+
+
+/**
+ * etchmbox_register_notify()
+ * @param pfx etch_mailbox_notify callback
+ * @param state an etch object relinquishes by caller, owned by mailbox,
+ * used to pass mailbox state on notification.
+ * @param maxdelay max time in milliseconds to wait for delivery, 0 forever, -1 no wait.
+ * @return 0 success, -1 failure.
+ */
+int etchmbox_register_notify (void* data, etch_mailbox_notify pfn,
+ etch_object* state, const int maxdelay)
+{
+ i_mailbox* ibox = (i_mailbox*)data;
+ int result = -1, isqueue_notempty_or_closed = FALSE;
+ etch_plainmailbox* thisx = ibox? ibox->thisx: NULL;
+ if (!thisx || !pfn || !state) return -1;
+
+ if (0 == etchqueue_lock(thisx->queue))
+ {
+ thisx->notify = pfn;
+ thisx->notify_state = state; /* caller relinquishes */
+
+ if (maxdelay > 0 && !thisx->is_alarm_set)
+ etchmbox_set_duration(thisx, maxdelay);
+
+ isqueue_notempty_or_closed
+ = etchqueue_size(thisx->queue) || etchqueue_is_closed(thisx->queue);
+
+ result = etchqueue_unlock(thisx->queue);
+ }
+
+ if (isqueue_notempty_or_closed)
+ etchmbox_fire_notify((etch_plainmailbox*)thisx);
+
+ return result;
+}
+
+
+/**
+ * etchmbox_unregister_notify()
+ */
+int etchmbox_unregister_notify (void* data, etch_mailbox_notify pfn)
+{
+ i_mailbox* ibox = (i_mailbox*)data;
+ int result = -1;
+ etch_plainmailbox* thisx = ibox? ibox->thisx: NULL;
+ if (!thisx || !pfn || pfn != thisx->notify) return -1;
+
+ if (0 == etchqueue_lock(thisx->queue))
+ {
+ if (thisx->is_alarm_set)
+ {
+ thisx->is_alarm_set = FALSE;
+
+ /* this signal will usually trigger a callback to the timer expiration
+ * handler, which will block until we release this lock, and then do
+ * nothing, since the reason code will indicate signaled rather than
+ * timer expiration. however if the timer were to have expired while
+ * we are here but before we signal it, the handler would again block,
+ * we would signal it below which would do nothing since the timer had
+ * already fired, and when we finally release the lock here, the handler
+ * would continue to presumably close the mailbox. if on the other hand
+ * the handler got the lock before we did, then the mailbox will now be
+ * closed when we finally get the lock.
+ */
+ etch_timer_stop(thisx->lifetimer);
+ }
+
+ thisx->notify = NULL;
+ etch_object_destroy(thisx->notify_state);
+ thisx->notify_state = NULL;
+
+
+ result = etchqueue_unlock(thisx->queue);
+ }
+
+ return result;
+}
+
+
+/**
+ * etchmbox_fire_notify()
+ * notify registered party of mailbox status change
+ */
+int etchmbox_fire_notify (void* data)
+{
+ etch_plainmailbox* thisx = (etch_plainmailbox*)data;
+ int result = -1, is_closed = 0;
+ etch_mailbox_notify notify = NULL;
+ etch_object* notify_state = NULL;
+
+ if (0 == etchqueue_lock(thisx->queue))
+ {
+ notify = thisx->notify;
+ is_closed = etchqueue_is_closed(thisx->queue);
+ notify_state = thisx->notify_state;
+
+ etchqueue_unlock(thisx->queue);
+ }
+
+ if (notify)
+ result = notify (thisx, thisx->imailbox, notify_state, is_closed);
+
+ return result;
+}
+
+
+/**
+ * etchmbox_is_empty()
+ */
+int etchmbox_is_empty (void* data)
+{
+ i_mailbox* ibox = (i_mailbox*)data;
+ int is_locked = FALSE, is_empty = TRUE;
+ etch_plainmailbox* thisx = ibox? ibox->thisx: NULL;
+
+ if (thisx)
+ { is_locked = 0 == etchqueue_trylock(thisx->queue);
+ is_empty = etchqueue_size(thisx->queue) == 0;
+ if (is_locked)
+ etchqueue_unlock(thisx->queue);
+ }
+
+ return is_empty;
+}
+
+
+/**
+ *
+ */
+int etchmbox_is_closed (void* data)
+{
+ i_mailbox* ibox = (i_mailbox*)data;
+ int is_locked = FALSE, is_closed = TRUE;
+ etch_plainmailbox* thisx = ibox? ibox->thisx: NULL;
+
+ if (thisx)
+ { is_locked = 0 == etchqueue_trylock(thisx->queue);
+ is_closed = etchqueue_is_closed(thisx->queue);
+ if (is_locked)
+ etchqueue_unlock(thisx->queue);
+ }
+
+ return is_closed;
+}
+
+
+/**
+ * etchmbox_is_full()
+ */
+int etchmbox_is_full (void* data)
+{
+ i_mailbox* ibox = (i_mailbox*)data;
+ int is_locked = FALSE, is_full = TRUE;
+ etch_plainmailbox* thisx = ibox? ibox->thisx: NULL;
+
+ if (thisx)
+ { is_locked = 0 == etchqueue_trylock(thisx->queue);
+ is_full = etchqueue_is_full(thisx->queue);
+ if (is_locked)
+ etchqueue_unlock(thisx->queue);
+ }
+
+ return is_full;
+}
+
+
+/**
+ * etchmbox_get_message_id()
+ */
+int64 etchmbox_get_message_id (void* data)
+{
+ i_mailbox* imailbox = (i_mailbox*)data;
+ etch_plainmailbox* thisx = (etch_plainmailbox*) imailbox->thisx;
+ return thisx? thisx->message_id: 0;
+}
+
+
+/* - - - - - - - - -
+ * private
+ * - - - - - - - - -
+*/
+
+/**
+ * etchmbox_is_alarm_set()
+ * used to test is_alarm_set from outside a queue lock only.
+ * do not use inside a queue lock, test is_alarm_set directly in that case.
+ * use from inside a queue lock will deadlock since the lock is non-recursive.
+ */
+int etchmbox_is_alarm_set(etch_plainmailbox* thisx)
+{
+ int result = FALSE;
+
+ if (0 == etchqueue_lock(thisx->queue))
+ {
+ result = thisx->is_alarm_set;
+ etchqueue_unlock(thisx->queue);
+ }
+
+ return result;
+}
+
+
+/**
+ * etchmbox_test_and_set_alarm_set()
+ * used to test and set is_alarm_set from outside a queue lock only.
+ * do not use inside a queue lock, set is_alarm_set directly in that case.
+ * use from inside a queue lock will deadlock since the lock is non-recursive
+ */
+int etchmbox_test_and_set_alarm_set(etch_plainmailbox* thisx, const int value)
+{
+ int result = FALSE;
+
+ if (0 == etchqueue_lock(thisx->queue))
+ {
+ result = thisx->is_alarm_set;
+ thisx->is_alarm_set = value? TRUE: FALSE;
+ etchqueue_unlock(thisx->queue);
+ }
+
+ return result;
+}
diff --git a/binding-c/runtime/c/src/main/transport/etch_plain_mailbox_manager.c b/binding-c/runtime/c/src/main/transport/etch_plain_mailbox_manager.c
new file mode 100644
index 0000000..fa4ffda
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_plain_mailbox_manager.c
@@ -0,0 +1,761 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_plainmailboxmgr.c
+ * mailbox manager accepts packets for potential delivery to a mailbox or to an
+ * alternate message handler if an appropriate mailbox is not available.
+ * a mailbox can be created on request keyed by the a message's ID.
+ */
+
+#include "etch_runtime.h"
+#include "etch.h"
+#include "etch_plain_mailbox_manager.h"
+#include "etch_plain_mailbox.h"
+#include "etch_objecttypes.h"
+#include "etch_thread.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+#include "etch_arrayval.h"
+
+static const char* LOG_CATEGORY = "etch_plain_mailbox_manager";
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+static int64 g_message_id = 0;
+static etch_mutex_t* g_message_id_mutex = NULL;
+
+// forward declarations
+static int64 etch_plain_mailbox_manager_generate_message_id();
+
+
+
+etch_plainmailboxmgr* new_plain_mailbox_manager_a(i_transportmessage*, const int, etch_mutex*);
+int destroy_plainmailboxmgr(void*);
+etch_hashtable* new_pmboxmgr_mailboxmap();
+etch_status_t etch_plain_mailbox_manager_shutdown_hook_func();
+
+int pmboxmgr_transport_call (void*, etch_who*, etch_message*, i_mailbox**);
+int pmboxmgr_redeliver (void*, etch_who*, etch_message*);
+int pmboxmgr_unregister (void*, i_mailbox*);
+int pmboxmgr_session_message (void*, etch_who*, etch_message*);
+int pmboxmgr_session_control (void*, etch_event*, etch_object*);
+int pmboxmgr_session_notify (void*, etch_event*);
+etch_object* pmboxmgr_session_query (void*, etch_query*);
+int pmboxmgr_transport_control(void*, etch_event*, etch_object*);
+int pmboxmgr_transport_notify (void*, etch_event*);
+etch_object* pmboxmgr_transport_query (void*, etch_query*);
+i_session* pmboxmgr_get_session(void*);
+
+
+/* - - - - - - - - - - - - - - -
+ * ctor/dtor
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_plain_mailbox_manager()
+ * etch_plainmailboxmgr public constructor
+ * @param itm i_transportmessage interface object, caller retains ownership.
+ * @param uri url string, caller relinquishes ownership.
+ * @param resxmap caller retains ownership.
+ * @param rwlock global mutex for mailbox read write sync, caller retains.
+ */
+etch_plainmailboxmgr* new_plain_mailbox_manager (i_transportmessage* itm,
+ wchar_t* uri, etch_resources* resxmap, etch_mutex* rwlock)
+{
+ etch_plainmailboxmgr* mgr = new_plain_mailbox_manager_a(itm, ETCH_INFWAIT, rwlock);
+ if (NULL == mgr) return NULL;
+
+ return mgr;
+}
+
+
+/**
+ * pmboxmgr_set_session()
+ * @param newsession an i_sessionmessage reference. caller owns this object.
+ * this will be invoked from delivery service constructor.
+ */
+void pmboxmgr_set_session (void* data, void* sessionData)
+{
+ etch_plainmailboxmgr* thisx = (etch_plainmailboxmgr*)data;
+ i_session* newsession = (i_session*)sessionData;
+ ETCH_ASSERT(is_etch_mailboxmgr(thisx));
+ ETCH_ASSERT(is_etch_sessionmsg(newsession));
+
+ /* set our session to passed (delivery service's) session */
+ thisx->session = (i_sessionmessage*)newsession;
+}
+
+/**
+ * pmboxmgr_transport_message()
+ * i_transportmessage::transport_message override.
+ * @param whoto, caller retains, can be null.
+ * @param msg caller abandons on success, retains on failure.
+ */
+int pmboxmgr_transport_message (void* data, void* whoData, void* messageData)
+{
+ etch_plainmailboxmgr* mgr = (etch_plainmailboxmgr*)data;
+ etch_who* whoto = (etch_who*)whoData;
+ etch_message* msg = (etch_message*)messageData;
+ int result = 0;
+
+ etch_int64* msgid = message_get_id(msg); /* expect NULL (not yet sent) */
+ if (NULL != msgid) return -1; /* already sent */
+
+ msgid = new_int64(etch_plain_mailbox_manager_generate_message_id()); /* assign ID to message */
+ result = message_set_id(msg, msgid); /* relinquish msgid object */
+
+ if (0 == result)
+ result = mgr->transport->transport_message (mgr->transport->thisx, whoto, msg);
+
+ return result;
+}
+
+
+/**
+ * new_plain_mailbox_manager_a()
+ * etch_plainmailboxmgr private constructor
+ * @param itm transportmessage interface, not owned.
+ * @param maxdelay maximum time in milliseconds to wait for a full mailbox
+ * @param rwlock global mutex for mailbox read write sync, caller retains.
+ * to become unfull before giving up and delivering to the message handler.
+ * ETCH_INFWAIT indicates wait forever, ETCH_NOWAIT indicates do not block.
+ */
+etch_plainmailboxmgr* new_plain_mailbox_manager_a (i_transportmessage* itm,
+ const int maxdelay, etch_mutex* rwlock)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_plainmailboxmgr* mgr = NULL;
+ i_mailbox_manager* imgr = NULL;
+ etch_hashtable* mboxmap = NULL;
+ i_transport* itransport = NULL;
+ i_session* isession = NULL;
+ etch_mutex* mutex = NULL; /* not used - lose it */
+ int result = -1;
+
+ do
+ {
+
+ if(g_message_id_mutex == NULL) {
+ // TODO: this should be removed, and added to .._module_initialize
+ // TODO: pool
+ status = etch_mutex_create(&g_message_id_mutex, ETCH_MUTEX_NESTED, NULL);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ ETCH_ASSERT(g_message_id_mutex != NULL);
+ etch_runtime_shutdown_hook_add(etch_plain_mailbox_manager_shutdown_hook_func);
+ g_message_id = apr_time_now();
+ }
+
+ // TODO: pool
+ status = etch_mutex_create(&mutex, ETCH_MUTEX_NESTED, NULL);
+ if(status != ETCH_SUCCESS) {
+ // error
+ break;
+ }
+ if (NULL == (mboxmap = new_pmboxmgr_mailboxmap())) break;
+
+ /* - - - - - - - - - - - - - - -
+ * etch_plainmailboxmgr
+ * - - - - - - - - - - - - - - -
+ */
+ mgr = (etch_plainmailboxmgr*) new_object(sizeof(etch_plainmailboxmgr), ETCHTYPEB_MBOXMGR_IMPL, CLASSID_PLAIN_MBOXMGR);
+
+ ((etch_object*)mgr)->destroy = destroy_plainmailboxmgr;
+ ((etch_object*)mgr)->clone = clone_null;
+ mgr->xlock = mutex; /* not used - lose it */
+ mgr->rwlock = rwlock; /* not owned */
+ mgr->mailboxes = mboxmap;
+ mgr->max_delay = maxdelay;
+
+ /* set our transport to that of next lower layer (messagizer) */
+ mgr->transport = itm; /* not owned */
+ /* note that mgr::transport::session has not been set yet. it will be set
+ * to mgr::session in mgr::set_session(), assuming that code invokes it.
+ * it is safer to leave mgr::transport::session null, than to point it to
+ * a session which is null. this effectively precludes code from setting
+ * mgr::session directly; code should use mgr::set_session() to do so.
+ */
+
+ /* - - - - - - - - - - - - - - -
+ * i_transportmessage (forward)
+ * - - - - - - - - - - - - - - -
+ */
+ itransport = new_transport_interface (mgr,
+ (etch_transport_control) pmboxmgr_transport_control,
+ (etch_transport_notify) pmboxmgr_transport_notify,
+ (etch_transport_query) pmboxmgr_transport_query);
+ itransport->get_session = pmboxmgr_get_session;
+ itransport->set_session = pmboxmgr_set_session;
+
+ /* instantiate i_transportmessage interface which messagizer implements */
+ mgr->transportmsg = new_transportmsg_interface (mgr,
+ pmboxmgr_transport_message,
+ itransport); /* transportmsg now owns itransport */
+
+ /* copy i_transportmessage interface methods up to messagizer */
+ mgr->transport_message = pmboxmgr_transport_message;
+ mgr->transport_control = itransport->transport_control;
+ mgr->transport_notify = itransport->transport_notify;
+ mgr->transport_query = itransport->transport_query;
+ mgr->get_session = itransport->get_session;
+ mgr->set_session = itransport->set_session;
+
+ /* - - - - - - - - - - - - - - -
+ * i_mailbox_manager
+ * - - - - - - - - - - - - - - -
+ */
+ imgr = new_mailboxmgr_interface (mgr, mgr->transportmsg, mgr->session);
+ mgr->imanager = imgr;
+ imgr->rwlock = rwlock;
+
+ imgr->transport_call = mgr->transport_call = (etch_mbm_transport_call)pmboxmgr_transport_call;
+ imgr->redeliver = mgr->redeliver = pmboxmgr_redeliver;
+ imgr->unregister = mgr->unregister = pmboxmgr_unregister;
+
+ /* i_mailbox_manager::i_transport */
+ imgr->transport_message = mgr->transport_message = pmboxmgr_transport_message;
+ imgr->transport_control = mgr->transport_control = pmboxmgr_transport_control;
+ imgr->transport_notify = mgr->transport_notify = pmboxmgr_transport_notify;
+ imgr->transport_query = mgr->transport_query = pmboxmgr_transport_query;
+ imgr->get_session = mgr->get_session = pmboxmgr_get_session;
+ imgr->set_session = mgr->set_session = pmboxmgr_set_session;
+
+ /* - - - - - - - - - - - - - - -
+ * i_sessionmessage (backward)
+ * - - - - - - - - - - - - - - -
+ */
+ isession = new_session_interface(mgr,
+ (etch_session_control) pmboxmgr_session_control,
+ (etch_session_notify) pmboxmgr_session_notify,
+ (etch_session_query) pmboxmgr_session_query);
+
+ /* instantiate the i_sessionmessage interface which mbox mgr implements */
+ mgr->isessionmsg = new_sessionmsg_interface(mgr,
+ pmboxmgr_session_message,
+ isession); /* isessionmsg now owns isession */
+
+ imgr->ism = mgr->isessionmsg; /* relinquish isessionmsg to imgr */
+
+ /* copy sessionmsg interface methods to parent */
+ mgr->session_message = imgr->session_message = pmboxmgr_session_message;
+ mgr->session_control = imgr->session_control = isession->session_control;
+ mgr->session_notify = imgr->session_notify = isession->session_notify;
+ mgr->session_query = imgr->session_query = isession->session_query;
+
+ /* finally set session of next lower layer (messagizer) to our session */
+ /* fyi mgr->transport->thisx is messagizer, the transport implementor */
+ if (mgr->transport) /* transport always supplied except by unit test */
+ mgr->transport->set_session (mgr->transport->thisx, mgr->isessionmsg);
+
+ result = 0;
+
+ } while(0);
+
+ if (-1 == result)
+ {
+ status = etch_mutex_destroy(mutex);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+
+ etch_object_destroy(mboxmap);
+ etch_object_destroy(imgr);
+ if (mgr)
+ etch_free(mgr);
+ mgr = NULL;
+ }
+
+ return mgr;
+}
+
+
+/**
+ * destroy_plainmailboxmgr()
+ * destructor for etch_plainmailboxmgr
+ */
+int destroy_plainmailboxmgr(void* data)
+{
+ etch_plainmailboxmgr* thisx = (etch_plainmailboxmgr*)data;
+ etch_iterator iterator;
+ int count = 1;
+ //signal interrupt to all mailboxes
+ set_iterator(&iterator, thisx->mailboxes, &thisx->mailboxes->iterable);
+ while(iterator.has_next(&iterator))
+ {
+ i_mailbox* mb = (i_mailbox*) iterator.current_value;
+ etch_apr_queue_interrupt_all(((etch_plainmailbox*)mb->thisx)->queue->aprq);
+ iterator.next(&iterator);
+ }
+
+ //wait until all mailboxes are unregistered
+ while(count != 0){
+ count = ((struct i_hashtable*)((etch_object*)thisx->mailboxes)->vtab)->count(thisx->mailboxes->realtable,0,0);
+ apr_sleep(1000 * 1000);
+ }
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ etch_mutex_destroy(thisx->xlock);
+ thisx->xlock = NULL;
+
+ etch_object_destroy(thisx->mailboxes);
+ thisx->mailboxes = NULL;
+
+ etch_object_destroy(thisx->imanager);
+ thisx->imanager = NULL;
+
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * i_sessionmessage
+ * - - - - - - - - - - - - - - -
+ */
+
+ /**
+ * get_etchmbm_session()
+ * convenience method to validate the etch_plainmailboxmgr, get, validate,
+ * and return the etch_plainmailboxmgr's session.
+ */
+i_sessionmessage* get_etchmbm_session (etch_plainmailboxmgr* mgr)
+{
+ i_sessionmessage* session = NULL;
+ ETCH_ASSERT(is_etch_mailboxmgr(mgr));
+ session = mgr->session;
+ ETCH_ASSERT(is_etch_sessionmsg(session));
+ return session;
+}
+
+
+/**
+ * pmboxmgr_session_message()
+ * queue specified message to the message's mailbox.
+ * @param whofrom caller retains, can be null.
+ * @param msg caller abandons on success (0), retains on other than success.
+ * @return 0 (message handled), or -1 (error, mailbox closed, or timeout)
+ */
+int pmboxmgr_session_message (void* data, etch_who* whofrom, etch_message* msg)
+{
+ etch_plainmailboxmgr* mgr = (etch_plainmailboxmgr*)data;
+ int result = 0;
+ i_mailbox* ibox = NULL;
+ etch_int64* msgid = NULL;
+ i_sessionmessage* session = get_etchmbm_session (mgr);
+
+ if(msg == NULL) {
+ return -1;
+ }
+
+ if (NULL == (msgid = message_get_in_reply_to (msg))) /* msgid not ours */
+ return session->session_message (session->thisx, whofrom, msg);
+
+ /* fyi no mailbox will exist for a one-way message */
+ ibox = pmboxmgr_get_mailbox (mgr, msgid);
+
+ /* todo ensure that caller destroys message on error return */
+ if (NULL == ibox || ibox->is_closed(ibox)) return -1;
+
+ result = ibox->message (ibox, whofrom, msg);
+
+ if (0 != result) /* ETCH_MAILBOX_TIMEOUT (-2) or mailbox closed (-1) */
+ result = -1;
+
+ return result;
+}
+
+
+/**
+ * pmboxmgr_session_control()
+ * i_sessionmsg::session_control override.
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+int pmboxmgr_session_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_plainmailboxmgr* mgr = (etch_plainmailboxmgr*)data;
+ i_sessionmessage* session = get_etchmbm_session (mgr);
+ return session->session_control(session->thisx, control, value);
+}
+
+
+/**
+ * pmboxmgr_session_notify()
+ * i_sessionmsg::session_notify override.
+ * @param event, caller relinquishes.
+ */
+int pmboxmgr_session_notify (void* data, etch_event* evt)
+{
+ etch_plainmailboxmgr* mgr = (etch_plainmailboxmgr*)data;
+ int result = 0;
+ const int event_type = evt->value;
+ i_sessionmessage* session = get_etchmbm_session (mgr);
+
+ switch(event_type)
+ { case ETCHEVT_SESSION_UP:
+ mgr->is_connection_up = TRUE;
+ break;
+ case ETCHEVT_SESSION_DOWN:
+ mgr->is_connection_up = FALSE;
+ result = pmboxmgr_unregister_all (mgr);
+ }
+
+ return session->session_notify (session->thisx, evt);
+}
+
+
+/**
+ * pmboxmgr_session_query()
+ * i_sessionmsg::session_query override.
+ * @param query, caller relinquishes.
+ */
+etch_object* pmboxmgr_session_query (void* data, etch_query* query)
+{
+ etch_plainmailboxmgr* mgr = (etch_plainmailboxmgr*)data;
+ i_sessionmessage* session = get_etchmbm_session (mgr);
+ return session->session_query (session->thisx, query);
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * i_transportmessage
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * get_etchmbm_transport()
+ * convenience method to validate the etch_plainmailboxmgr, get, validate,
+ * and return the etch_plainmailboxmgr's transport.
+ */
+i_transportmessage* get_etchmbm_transport(etch_plainmailboxmgr* mgr)
+{
+ i_transportmessage* transport = NULL;
+ ETCH_ASSERT(is_etch_mailboxmgr(mgr));
+ transport = mgr->transport;
+ ETCH_ASSERT(is_etch_transportmsg(transport));
+ return transport;
+}
+
+
+/**
+ * pmboxmgr_transport_control()
+ * i_transportmessage::transport_control override.
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+int pmboxmgr_transport_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_plainmailboxmgr* mgr = (etch_plainmailboxmgr*)data;
+ i_transportmessage* transport = get_etchmbm_transport(mgr);
+ return transport->transport_control (transport->thisx, control, value);
+}
+
+
+/**
+ * pmboxmgr_transport_notify()
+ * i_transportmessage::transport_notify override.
+ * @param evt, caller relinquishes.
+ */
+int pmboxmgr_transport_notify (void* data, etch_event* evt)
+{
+ etch_plainmailboxmgr* mgr = (etch_plainmailboxmgr*)data;
+ i_transportmessage* transport = get_etchmbm_transport(mgr);
+ return transport->transport_notify(transport->thisx, evt);
+}
+
+
+/**
+ * pmboxmgr_transport_query()
+ * i_transportmessage::transport_query override.
+ * @param query, caller relinquishes.
+ */
+etch_object* pmboxmgr_transport_query (void* data, etch_query* query)
+{
+ etch_plainmailboxmgr* mgr = (etch_plainmailboxmgr*)data;
+ i_transportmessage* transport = get_etchmbm_transport(mgr);
+ return transport->transport_query(transport->thisx, query);
+}
+
+
+/**
+ * pmboxmgr_get_session()
+ * @return a reference to the messagizer i_sessionmessage interface.
+ * this object is owned by whatever object set the messagizer session.
+ */
+i_session* pmboxmgr_get_session (void* data)
+{
+ etch_plainmailboxmgr* mgr = (etch_plainmailboxmgr*)data;
+ ETCH_ASSERT(is_etch_mailboxmgr(mgr));
+ return (i_session*)mgr->session;
+}
+
+
+
+
+/* - - - - - - - - - - - - - - -
+ * i_mailbox_manager
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * pmboxmgr_transport_call()
+ */
+ int pmboxmgr_transport_call (void* data, etch_who* whoto,
+ etch_message* msg, i_mailbox** out)
+{
+ i_mailbox_manager* imgr = (i_mailbox_manager*)data;
+ int result = 0;
+ i_mailbox* ibox = NULL;
+ etch_plainmailbox* mbox = NULL;
+ etch_plainmailboxmgr* mgr = NULL;
+ etch_int64 *msgid = NULL, *inreplyto = NULL;
+ const int MAXMESSAGES_ONE = 1, lifetime = MBOX_LIFETIME_UNTIL_CLOSE;
+ ETCH_ASSERT(is_etch_imailboxmgr(imgr));
+ mgr = imgr->thisx;
+ ETCH_ASSERT(is_etch_mailboxmgr(mgr));
+
+ msgid = message_get_id(msg); /* expect NULL (not yet sent) */
+ inreplyto = message_get_in_reply_to(msg);
+ if (NULL != msgid) return -1; /* already sent */
+ if (NULL != inreplyto) return -1; /* marked as reply */
+
+ msgid = new_int64(etch_plain_mailbox_manager_generate_message_id());
+ result = message_set_id(msg, msgid); /* assign ID to message */
+ if (0 != result) return -1;
+
+ mbox = new_mailbox (imgr, msgid->value, mgr->max_delay, lifetime, MAXMESSAGES_ONE);
+
+ if (mbox) ibox = mbox->imailbox;
+ if (NULL == ibox) return -1;
+
+ result = pmboxmgr_register_mailbox (mgr, ibox);
+
+ /* currently lifetime is always zero (until explictly closed); however if
+ * lifetime were to be > 0, a timer would enter the picture, and since we
+ * do not currently have a proper timer pool, our simple timer thread
+ * might need a start cushion.
+ */
+ if (0 == result && lifetime > 0)
+ etch_sleep(100);
+
+ if (0 == result)
+ result = mgr->transport->transport_message (mgr->transport->thisx, whoto, msg);
+
+ if (0 == result)
+ if (out)
+ *out = ibox;
+ else;
+ else
+ pmboxmgr_unregister(imgr, ibox);
+
+ return result;
+}
+
+
+/**
+ * pmboxmgr_redeliver()
+ * forwards specified message
+ */
+int pmboxmgr_redeliver (void* data, etch_who* whofrom, etch_message* msg)
+{
+ i_mailbox_manager* imgr = (i_mailbox_manager*)data;
+ etch_iterator iterator;
+ etch_hashtable* map = NULL;
+ etch_plainmailboxmgr* mgr = NULL;
+ ETCH_ASSERT(is_etch_imailboxmgr(imgr));
+ mgr = imgr->thisx;
+ ETCH_ASSERT(is_etch_mailboxmgr(mgr));
+
+ map = mgr->mailboxes;
+ hashtable_getlock(map);
+ set_iterator(&iterator, map, &map->iterable);
+
+ while(iterator.has_next(&iterator))
+ { /* TODO if change i_mailbox to be etch_object based, check class_id here */
+ i_mailbox* ibox = (i_mailbox*) iterator.current_value;
+ if (ibox)
+ ibox->close_delivery(ibox);
+
+ iterator.next(&iterator);
+ }
+
+ hashtable_rellock(map);
+ return 0;
+}
+
+
+/**
+ * pmboxmgr_unregister()
+ * remove specified mailbox from manager.
+ * the unregistered box is *not* destroyed - whoever registered it owns it.
+ */
+int pmboxmgr_unregister (void* data, i_mailbox* ibox)
+{
+ i_mailbox_manager* imgr = (i_mailbox_manager*)data;
+ int64 msgid;
+ unsigned hashval;
+ etch_hashtable* map;
+ etch_int64* itemkey;
+ etch_hashitem hi, *removed = &hi;
+ etch_plainmailboxmgr* mgr = imgr? imgr->thisx: NULL;
+ if (NULL == mgr) return -1;
+
+ map = mgr->mailboxes;
+ msgid = ibox->get_message_id(ibox);
+ hashval = etchhash(&msgid, sizeof(int64), 0);
+
+ if (-1 == ((struct i_hashtable*)((etch_object*)map)->vtab)->removeh (map->realtable, hashval, map, (void**)&removed))
+ return -1;
+ itemkey = (etch_int64*) removed->key;
+ ETCH_ASSERT(is_etch_int64(itemkey));
+ etch_free(itemkey); /* once we remove the entry, we own the key */
+ return 0;
+}
+
+
+/* - - - - - - - - - - - - - -
+ * etch_plaingmboxmgr public
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * pmboxmgr_register_mailbox()
+ * add specified mailbox to set of mailboxes receiving responses to messages.
+ * @param ibox caller retains ownership.
+ * TODO does mgr need to own and destroy mailboxes? if so, i_mailbox
+ * destructor must destroy its parent object. maybe it needs to do this anyway.
+ */
+int pmboxmgr_register_mailbox(etch_plainmailboxmgr* mgr, i_mailbox* ibox)
+{
+ int result = -1;
+
+ if (mgr->is_connection_up)
+ { etch_hashtable* map = mgr->mailboxes;
+ int64 message_id = ibox->get_message_id(ibox);
+ etch_int64* idobj = new_int64(message_id);
+ /* insert mailbox to synchronized map */
+ ((etch_object*)idobj)->get_hashkey(idobj);
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->inserth(map->realtable, idobj, ibox, map, 0);
+ if (-1 == result) etch_free(idobj); /* duplicate id */
+ }
+ else
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "attempt to register disconnected mailbox\n");
+
+ return result;
+}
+
+
+/**
+ * pmboxmgr_get_mailbox()
+ * return interface to mailbox having specified message id, or NULL.
+ */
+ i_mailbox* pmboxmgr_get_mailbox(etch_plainmailboxmgr* mgr, etch_int64* msgid)
+{
+ etch_hashtable* map = mgr->mailboxes;
+ etch_hashitem hi, *entry = &hi;
+
+ const int result = ((struct i_hashtable*)((etch_object*)map)->vtab)->findh(map->realtable, ((etch_object*)msgid)->get_hashkey(msgid), map, (void**)&entry);
+ return result == 0? (i_mailbox*) entry->value: NULL;
+}
+
+
+/**
+ * pmboxmgr_unregister_all()
+ * unregisters all mailboxes
+ */
+ int pmboxmgr_unregister_all(etch_plainmailboxmgr* mgr)
+{
+ etch_hashtable* map = mgr->mailboxes;
+ etch_iterator iterator;
+ hashtable_getlock(map);
+ set_iterator(&iterator, map, &map->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ i_mailbox* ibox = (i_mailbox*) iterator.current_value;
+ if (ibox)
+ ibox->close_delivery(ibox);
+
+ iterator.next(&iterator);
+ }
+
+ hashtable_rellock(map);
+ return 0;
+}
+
+
+/**
+ * pmboxmgr_size()
+ * return count of registered mailboxes
+ */
+ int pmboxmgr_size(etch_plainmailboxmgr* mgr)
+{
+ etch_hashtable* map = mgr->mailboxes;
+ return ((struct i_hashtable*)((etch_object*)map)->vtab)->count(map->realtable, map, 0);
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * etch_plaingmboxmgr private
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_pmboxmgr_mailboxmap()
+ * construct and return a hashtable configured as expected for the mailbox map.
+ * this map's destructor will destroy neither key objects nor value objects.
+ */
+etch_hashtable* new_pmboxmgr_mailboxmap()
+{
+ etch_hashtable* map = new_hashtable_synchronized(ETCHMBMGR_DEFNUMMAILBOXES);
+ map->content_type = ETCHHASHTABLE_CONTENT_OBJECT_OBJECT;
+ map->is_readonly_keys = FALSE; /* owns key objects */
+ map->is_readonly_values = TRUE; /* does not own mailbox */
+ return map;
+}
+
+
+
+
+etch_status_t etch_plain_mailbox_manager_shutdown_hook_func()
+{
+ if(g_message_id_mutex != NULL) {
+ etch_mutex_destroy(g_message_id_mutex);
+ g_message_id_mutex = NULL;
+ }
+ return ETCH_SUCCESS;
+}
+
+/**
+ * etch_generate_message_id()
+ * atomically generate a new message ID
+ */
+static int64 etch_plain_mailbox_manager_generate_message_id()
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int64 message_id = 0;
+
+ status = etch_mutex_lock(g_message_id_mutex);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ message_id = ++g_message_id;
+ status = etch_mutex_unlock(g_message_id_mutex);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+ return message_id;
+}
diff --git a/binding-c/runtime/c/src/main/transport/etch_queue.c b/binding-c/runtime/c/src/main/transport/etch_queue.c
new file mode 100644
index 0000000..e59b1ea
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_queue.c
@@ -0,0 +1,445 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * etch_queue.c
+ */
+
+#include "etch_queue.h"
+#include "etch_thread.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+#include "etch_runtime.h"
+#include "etch_objecttypes.h"
+
+/*
+static const char* LOG_CATEGORY = "etch_queue";
+*/
+
+extern apr_pool_t* g_etch_main_pool;
+
+int destroy_etch_queue(void*);
+void etchqueue_clear(etch_queue*);
+int etchqueue_iterable_first(etch_iterator*);
+int etchqueue_iterable_next(etch_iterator*);
+int etchqueue_iterable_has_next(etch_iterator*);
+
+
+
+/**
+ * new_queue()
+ * etch_queue constructor
+ */
+etch_queue* new_queue(const int initialsize)
+{
+ etch_queue* newq = NULL;
+ etch_apr_queue_t* aprq = NULL;
+ apr_pool_t* newsubpool = NULL;
+ int result = -1, qsize = initialsize > 0? initialsize: ETCH_DEFQSIZE;
+
+ do
+ {
+
+ // TODO: pool
+ result = apr_pool_create(&newsubpool, g_etch_main_pool);
+ //printf("2 creating apr pool %p\n",newsubpool);
+ if (0 != result) break;
+ /* set pool abort function */
+ apr_pool_abort_set(etch_runtime_mem_abort, newsubpool);
+
+
+ result = etch_apr_queue_create (&aprq, qsize, newsubpool);
+ if (0 != result) break;
+
+ newq = (etch_queue*) new_object(sizeof(etch_queue), ETCHTYPEB_ETCHQUEUE, CLASSID_ETCHQUEUE);
+
+ newq->aprq = aprq;
+ newq->subpool = newsubpool;
+ newq->qcapacity = initialsize;
+ ((etch_object*)newq)->destroy = destroy_etch_queue;
+ /* assume content to be etch objects owned by the queue */
+ newq->is_readonly = FALSE;
+ newq->content_type = ETCHQUEUE_CONTENT_OBJECT;
+
+ new_iterable(&newq->iterable, NULL, etchqueue_iterable_first,
+ etchqueue_iterable_next, etchqueue_iterable_has_next);
+
+ result = 0;
+
+ } while(0);
+
+ if (0 != result)
+ {
+ if (newsubpool) {
+ //printf("3 destroying apr pool %p\n",newsubpool);
+ apr_pool_destroy(newsubpool);
+ }
+ }
+
+ return newq;
+}
+
+
+/*
+ * destroy_etch_queue()
+ * etch_queue destructor
+ */
+int destroy_etch_queue(void* data)
+{
+ etch_queue* queue = (etch_queue*)data;
+ if (NULL == queue) return -1;
+
+ if (-1 == etch_apr_queue_trylock(queue->aprq))
+ return -1;
+ else etch_apr_queue_unlock(queue->aprq);
+
+ if (!is_etchobj_static_content(queue))
+ {
+ /* free etch-managed memory */
+ etchqueue_clear(queue);
+
+ /* free APR-managed memory - APR queue sets a callback such that
+ * destroying the memory pool destroys the APR queue object */
+ if (queue->subpool) {
+ //printf("4 destroying apr pool %p\n",queue->subpool);
+ apr_pool_destroy(queue->subpool);
+ }
+
+ queue->subpool = NULL;
+ queue->aprq = NULL;
+ }
+
+ return destroy_objectex((etch_object*)queue);
+}
+
+
+/*
+ * etchqueue_clear()
+ * frees queue content memory.
+ * this ensures the underlying queue is closed, so it is only invoked prior to
+ * destruction of the queue in order to free any remaining queued etch objects.
+ * all other memory associated with the queue, other than the shell object,
+ * is managed by APR.
+ */
+void etchqueue_clear(etch_queue* thisx)
+{
+ if (NULL == thisx || NULL == thisx->aprq || thisx->is_readonly) return;
+ etchqueue_close(thisx, TRUE); /* close queue and notify all waiters */
+
+ do
+ { void* qobj = NULL;
+ queuecallback callback = thisx->freehook;
+ int i = 0, is_freehandled = 0, result = 0;
+ const int is_obj_content = thisx->content_type == ETCHQUEUE_CONTENT_OBJECT;
+
+ while(1)
+ {
+ result = etchqueue_get_withwait (thisx, ETCHQUEUE_CLEARING_CLOSED_QUEUE, &qobj);
+ if (0 != result) break;
+
+ is_freehandled = callback? callback(i, qobj): FALSE;
+
+ if (is_freehandled);
+ else
+ if (is_obj_content)
+ ((etch_object*)qobj)->destroy(qobj);
+ else etch_free(qobj);
+ }
+
+ } while(0);
+}
+
+
+/**
+ * etchqueue_size()
+ * @return current queue depth
+ * not thread-safe, caller should hold the mailbox queue lock
+ */
+int etchqueue_size(etch_queue* thisx)
+{
+ return thisx && thisx->aprq? etch_apr_queue_size(thisx->aprq): 0;
+}
+
+
+/**
+ * etchqueue_is_closed()
+ * not thread-safe, caller should hold the mailbox queue lock
+ */
+int etchqueue_is_closed(etch_queue* thisx)
+{
+ return thisx && thisx->aprq? thisx->aprq->terminated != 0: TRUE;
+}
+
+
+/**
+ * etchqueue_is_full()
+ * not thread-safe, caller should hold the mailbox queue lock
+ */
+int etchqueue_is_full(etch_queue* thisx)
+{
+ return thisx && thisx->aprq? thisx->aprq->nelts == thisx->aprq->bounds: TRUE;
+}
+
+
+/**
+ * etchqueue_close()
+ * underlying implementation ensures operation atomicity
+ */
+int etchqueue_close(etch_queue* thisx, const int is_needlock)
+{
+ int result = 0;
+
+ if (!etchqueue_is_closed(thisx))
+ {
+ if (is_needlock)
+ result = etch_apr_queue_term(thisx->aprq);
+ else
+ result = etch_apr_queue_unsafeclose(thisx->aprq);
+
+ result = result == 0? 0: -1;
+ }
+ return result;
+}
+
+
+/**
+ * etchqueue_put()
+ * push specified item onto the queue. if the queue is full,
+ * wait indefinitely until space becomes available.
+ * @return 0 success, -1 error.
+ */
+int etchqueue_put(etch_queue* thisx, void* item)
+{
+ const int result = etchqueue_put_withwait(thisx, ETCH_INFWAIT, item);
+ return result;
+}
+
+
+/**
+ * etchqueue_put_withwait()
+ * push specified item onto the queue. if the queue is full,
+ * waitms specified time interval until space becomes available.
+ * @param waitms wait interval in milliseconds. specify ETCH_INFWAIT to wait
+ * forever, ETCH_NOWAIT to not wait.
+ * @return 0 success, -1 unsuccessful.
+ *
+ */
+int etchqueue_put_withwait(etch_queue* thisx, const int waitms, void* item)
+{
+ int result = 0, aprresult = 0;
+ const int64 wait_usec = waitms < 0? -1: waitms * 1000;
+ if (NULL == item || etchqueue_is_closed(thisx)) return -1;
+
+ /*
+ * etch_apr_queue_push() takes care of notifying threads waiting on a
+ * queue slot to become available, if any.
+ */
+ aprresult = etch_apr_queue_push (thisx->aprq, wait_usec, item);
+
+ switch(aprresult)
+ { case APR_SUCCESS: break;
+
+ case APR_EAGAIN: /* timed out and queue still full */
+ case APR_TIMEUP:
+ result = ETCH_QUEUE_OPERATION_TIMEOUT;
+ break;
+
+ case APR_EINTR: /* wait interrupted and queue still full */
+ result = ETCH_QUEUE_OPERATION_CANCELED;
+ break;
+
+ case APR_EOF: /* queue closed */
+ default: /* some error */
+ result = -1;
+ }
+
+ return result;
+}
+
+
+/**
+ * etchqueue_get()
+ * pop specified item off the queue and return it. if the queue is empty,
+ * wait indefinitely for an item to arrive. popped item is returned in
+ * out parameter.
+ * @return 0 success, -1 error.
+ */
+int etchqueue_get(etch_queue* thisx, void** itemout)
+{
+ const int result = etchqueue_get_withwait(thisx, ETCH_INFWAIT, itemout);
+ return result;
+}
+
+
+/**
+ * etchqueue_get_withwait()
+ * pop specified item off the queue and return it. if the queue is empty,
+ * wait specified time interval for an item to arrive. popped item is
+ * returned in out parameter.
+ * @param waitms wait interval in milliseconds. specify ETCH_INFWAIT to wait
+ * forever, ETCH_NOWAIT to not wait, ETCHQUEUE_CLEARING_CLOSED_QUEUE to do a
+ * get with no wait regardless of whether queue has been closed.
+ * @return 0 success, -1 unsuccessful.
+ *
+ */
+int etchqueue_get_withwait(etch_queue* thisx, const int waitms, void** itemout)
+{
+ int result = 0, aprresult = 0;
+ const int64 wait_usec = waitms < 0? waitms: waitms * 1000;
+
+ if (etchqueue_is_closed(thisx))
+ if (waitms != ETCHQUEUE_CLEARING_CLOSED_QUEUE)
+ return -1;
+
+ /*
+ * etch_apr_queue_pop() takes care of notifying waiters if necessary
+ */
+ aprresult = etch_apr_queue_pop (thisx->aprq, wait_usec, itemout);
+
+ switch(aprresult)
+ { case APR_SUCCESS: break;
+
+ case APR_EAGAIN: /* timed out and queue still full */
+ case APR_TIMEUP:
+ result = ETCH_QUEUE_OPERATION_TIMEOUT; break;
+
+ case APR_EINTR: /* wait interrupted and queue still empty */
+ result = ETCH_QUEUE_OPERATION_CANCELED; break;
+
+ case APR_EOF: /* queue empty */
+ result = ETCH_QUEUE_EOF; break;
+
+ default: /* some error */
+ result = -1;
+ }
+
+ return result;
+}
+
+
+/**
+ * etchqueue_try_put()
+ * @return 0 success, -1 unsuccessful.
+ *
+ */
+int etchqueue_try_put(etch_queue* thisx, void* item)
+{
+ const int result = etch_apr_queue_trypush(thisx->aprq, item);
+ return result == 0? 0: -1;
+}
+
+
+/**
+ * etchqueue_try_get()
+ * @return 0 success, -1 unsuccessful.
+ */
+int etchqueue_try_get(etch_queue* thisx, void** itemout)
+{
+ const int result = etch_apr_queue_trypop(thisx->aprq, itemout);
+ return result == 0? 0: -1;
+}
+
+
+ /**
+ * etchqueue_notify_all()
+ * wake up all waiters,
+ * @return 0 success, -1 unsuccessful.
+ */
+int etchqueue_notify_all(etch_queue* thisx)
+{
+ const int aprresult = etch_apr_queue_interrupt_all(thisx->aprq);
+ return aprresult == 0? 0: -1;
+}
+
+
+/**
+ * acquire the lock guarding the queue
+ */
+int etchqueue_lock(etch_queue* thisx)
+{
+ return 0 == etch_apr_queue_lock(thisx->aprq)? 0: -1;
+}
+
+
+/**
+ * release the lock guarding the queue
+ */
+int etchqueue_unlock(etch_queue* thisx)
+{
+ return 0 == etch_apr_queue_unlock(thisx->aprq)? 0: -1;
+}
+
+
+/**
+ * acquire the lock guarding the queue if currently availbale
+ */
+int etchqueue_trylock(etch_queue* thisx)
+{
+ return 0 == etch_apr_queue_trylock(thisx->aprq)? 0: -1;
+}
+
+
+/* - - - - - - - - - -
+ * i_iterable
+ * - - - - - - - - - -
+ */
+
+/*
+ * etchqueue_iterable_first()
+ * i_iterable first() implementation
+ */
+int etchqueue_iterable_first(etch_iterator* iter)
+{
+ etch_queue* etchq = NULL;
+ if (!iter || !iter->collection) return -1;
+ etchq = iter->collection;
+ if (!etchqueue_size(etchq)) return -1;
+
+ iter->current_value = etchq->aprq->data[0];
+ iter->ordinal = iter->current_value? 1: 0;
+ return iter->ordinal? 0: -1;
+}
+
+
+/*
+ * etchqueue_iterable_next()
+ * i_iterable next() implementation
+ * functions as first() if there is no current position.
+ */
+int etchqueue_iterable_next(etch_iterator* iter)
+{
+ etch_queue* etchq = iter? iter->collection: NULL;
+ const int count = etchqueue_size(etchq);
+ if (!count || !iter->ordinal) return -1;
+
+ iter->current_value = etchq->aprq->data[iter->ordinal];
+ iter->ordinal = iter->current_value? ++iter->ordinal: 0;
+ return iter->ordinal? 0: -1;
+}
+
+
+/*
+ * etchqueue_iterable_has_next()
+ * i_iterable has_next() implementation.
+ */
+int etchqueue_iterable_has_next(etch_iterator* iter)
+{
+ etch_queue* etchq = iter? iter->collection: NULL;
+ const int count = etchqueue_size(etchq);
+ return count && iter->ordinal && (iter->ordinal <= count);
+}
diff --git a/binding-c/runtime/c/src/main/transport/etch_session_data.c b/binding-c/runtime/c/src/main/transport/etch_session_data.c
new file mode 100644
index 0000000..71d30d0
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_session_data.c
@@ -0,0 +1,87 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sessiondata.c
+ * i_sessiondata interface
+ */
+
+#include "etch_session_data.h"
+#include "etch_flexbuffer.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+/*
+static const char* LOG_CATEGORY = "etch_session_data";
+*/
+
+/*
+ * destroy_sessiondata()
+ * i_sessiondata destructor
+ */
+int destroy_sessiondata(void* data)
+{
+ i_sessiondata* sm = (i_sessiondata*)data;
+ if (NULL == sm) return -1;
+
+ if (!is_etchobj_static_content(sm))
+ { etch_free(sm->isession);
+ }
+
+ return destroy_objectex((etch_object*)sm);
+}
+
+
+/*
+ * etch_msghandler_defmessage()
+ * default virtual implementation
+ * @param sender caller retains
+ * @param buf caller retains
+ */
+int etchsessiondata_def_sessiondata (void* data, void* whoData, void* bufferData)
+{
+ return -1;
+}
+
+
+/**
+ * new_sessiondata_interface()
+ * i_sessiondata constructor
+ * @param sm i_sessiondata::session_data() virtual function override.
+ * caller relinquishes ownership of this memory
+ * @param isession session interface virtual function overrides.
+ * caller relinquishes ownership of this memory
+ */
+i_sessiondata* new_sessiondata_interface(void* thisx, etch_session_data sm, i_session* isession)
+{
+ i_sessiondata* newi = (i_sessiondata*) new_object(sizeof(i_sessiondata), ETCHTYPEB_SESSIONDATA, CLASSID_SESSIONDATA);
+
+ newi->thisx = thisx;
+ ((etch_object*)newi)->clone = clone_null;
+ ((etch_object*)newi)->destroy = destroy_sessiondata;
+
+ newi->session_data = sm ? sm : etchsessiondata_def_sessiondata;
+
+ newi->isession = isession? isession: new_default_session_interface(thisx);
+ newi->session_control = newi->isession->session_control;
+ newi->session_notify = newi->isession->session_notify;
+ newi->session_query = newi->isession->session_query;
+
+ return newi;
+}
diff --git a/binding-c/runtime/c/src/main/transport/etch_session_listener.c b/binding-c/runtime/c/src/main/transport/etch_session_listener.c
new file mode 100644
index 0000000..398009e
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_session_listener.c
@@ -0,0 +1,102 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sessionlisten.c
+ * i_sessionlistener interface
+ */
+
+#include "etch_session_listener.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+/*
+static const char* LOG_CATEGORY = "etch_session_listener";
+*/
+int etchseslxr_def_session_accepted (void* thisx, void*);
+
+
+/*
+ * destroy_sessionlistener()
+ * i_sessionlistener destructor
+ */
+int destroy_sessionlistener(void* data)
+{
+ i_sessionlistener* sa = (i_sessionlistener*)data;
+ if (NULL == sa) return -1;
+
+ if (!is_etchobj_static_content(sa))
+ {
+ if (sa->is_session_owned)
+ etch_free(sa->isession);
+
+ if (sa->is_transport_owned)
+ etch_free(sa->itransport);
+
+ // ETCHOBJ_DESTROY();
+ if(((etch_object*)sa->url))
+ ((etch_object*)sa->url)->destroy(((etch_object*)sa->url));
+ sa->url = NULL;
+
+ }
+
+ return destroy_objectex((etch_object*)sa);
+}
+
+
+/**
+ * new_sessionlistener_interface()
+ * i_sessionlistener constructor
+ * @params sa server on accept callback confirming to typedef etch_session_accepted.
+ * @param isession session interface virtual function overrides,
+ * caller relinquishes ownership of this memory.
+ */
+i_sessionlistener* new_sessionlistener_interface (void* thisx, etch_session_accepted sa, i_session* isession)
+{
+ i_sessionlistener* newi = (i_sessionlistener*) new_object
+ (sizeof(i_sessionlistener), ETCHTYPEB_SESSIONLXR, CLASSID_SESSIONLXR);
+
+ newi->thisx = thisx;
+ ((etch_object*)newi)->clone = clone_null;
+ ((etch_object*)newi)->destroy = destroy_sessionlistener;
+
+ newi->session_accepted = sa? sa: etchseslxr_def_session_accepted;
+
+ newi->isession = isession? isession: new_default_session_interface(thisx);
+ newi->isession->thisx = newi;
+ newi->session_control = newi->isession->session_control;
+ newi->session_notify = newi->isession->session_notify;
+ newi->session_query = newi->isession->session_query;
+ newi->is_session_owned = TRUE;
+
+ newi->itransport = new_default_transport_interface(thisx);
+ newi->is_transport_owned = TRUE;
+
+ return newi;
+}
+
+
+
+/*
+ * etchseslxr_def_session_accepted()
+ * @param socket caller retains
+ */
+int etchseslxr_def_session_accepted (void* thisx, void* socket)
+{
+ return -1;
+}
diff --git a/binding-c/runtime/c/src/main/transport/etch_session_message.c b/binding-c/runtime/c/src/main/transport/etch_session_message.c
new file mode 100644
index 0000000..7b4a145
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_session_message.c
@@ -0,0 +1,94 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sessionmsg.c
+ * i_sessionmessage interface
+ */
+
+#include "etch_session_message.h"
+#include "etch_message.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+/*
+static const char* LOG_CATEGORY = "etch_session_message";
+*/
+/*
+ * destroy_sessionmessage()
+ * i_sessionmessage destructor
+ */
+int destroy_sessionmessage(void* data)
+{
+ i_sessionmessage* sm = (i_sessionmessage*)data;
+ if (NULL == sm) return -1;
+
+ if (!is_etchobj_static_content(sm))
+ { etch_free(sm->isession);
+ }
+
+ return destroy_objectex((etch_object*)sm);
+}
+
+
+/*
+ * etch_msghandler_defmessage()
+ * default virtual implementation
+ * @param sender caller retains
+ * @param msg caller retains
+ * memory management rules are: if session_message handles the message, it owns
+ * msg memory. otherwise, if not handled, msg is retained by the caller for
+ * redirection via session_notify destination.
+ */
+int etchsessionmsg_def_sessionmessage (void* sm, etch_who* sender, etch_message* msg)
+{
+ return -1;
+}
+
+
+/**
+ * new_sessionmsg_interface()
+ * i_sessionmessage constructor
+ * @param isession session interface virtual function overrides,
+ * caller relinquishes ownership of this memory
+ * @param itransport transport interface virtual function overrides,
+ * caller relinquishes ownership of this memory
+ */
+i_sessionmessage* new_sessionmsg_interface(void* thisx, etch_session_message sm, i_session* isession)
+{
+ i_sessionmessage* newi = (i_sessionmessage*) new_object
+ (sizeof(i_sessionmessage), ETCHTYPEB_SESSIONMSG, CLASSID_SESSIONMSG);
+
+ newi->thisx = thisx;
+ ((etch_object*)newi)->clone = clone_null;
+ ((etch_object*)newi)->destroy = destroy_sessionmessage;
+
+ if (sm) {
+ newi->session_message = sm;
+ } else {
+ newi->session_message = etchsessionmsg_def_sessionmessage;
+ }
+
+ newi->isession = isession? isession: new_default_session_interface(thisx);
+ newi->session_control = newi->isession->session_control;
+ newi->session_notify = newi->isession->session_notify;
+ newi->session_query = newi->isession->session_query;
+
+ return newi;
+}
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_session_packet.c b/binding-c/runtime/c/src/main/transport/etch_session_packet.c
new file mode 100644
index 0000000..8c76a96
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_session_packet.c
@@ -0,0 +1,90 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_sessionpkt.c
+ * i_sessionpacket interface
+ */
+
+#include "etch_session_packet.h"
+#include "etch_message.h"
+#include "etch_flexbuffer.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+/*
+static const char* LOG_CATEGORY = "etch_session_package";
+*/
+
+/*
+ * destroy_sessionpacket()
+ * i_sessionpacket destructor
+ */
+int destroy_sessionpacket(void* data)
+{
+ i_sessionpacket* sm = (i_sessionpacket*)data;
+ if (NULL == sm) return -1;
+
+ if (!is_etchobj_static_content(sm))
+ { etch_free(sm->isession);
+ }
+
+ return destroy_objectex((etch_object*)sm);
+}
+
+
+/*
+ * etchsessionpkt_def_sessionpacket()
+ * default virtual implementation
+ * @param sender caller retains
+ * @param buf caller retains
+ */
+int etchsessionpkt_def_sessionpacket (void* data, void* whoData, void* bufferData)
+{
+ return -1;
+}
+
+
+/**
+ * new_sessionpkt_interface()
+ * i_sessionpacket constructor
+ * @param sp session_packetvirtual function overrides,
+ * @param itransport transport interface virtual function overrides,
+ * caller relinquishes ownership of this memory
+ */
+i_sessionpacket* new_sessionpkt_interface(void* thisx, etch_session_packet sp, i_session* isession)
+{
+ i_sessionpacket* newi = (i_sessionpacket*) new_object (sizeof(i_sessionpacket), ETCHTYPEB_SESSIONPKT, CLASSID_SESSIONPKT);
+
+ newi->thisx = thisx;
+ ((etch_object*)newi)->clone = clone_null;
+ ((etch_object*)newi)->destroy = destroy_sessionpacket;
+
+ newi->session_packet = sp ? sp: etchsessionpkt_def_sessionpacket;
+
+ newi->isession = isession? isession: new_default_session_interface(thisx);
+ newi->session_control = newi->isession->session_control;
+ newi->session_notify = newi->isession->session_notify;
+ newi->session_query = newi->isession->session_query;
+
+ return newi;
+}
+
+
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_tcp_connection.c b/binding-c/runtime/c/src/main/transport/etch_tcp_connection.c
new file mode 100644
index 0000000..8d0b8f4
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_tcp_connection.c
@@ -0,0 +1,1062 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_tcpconxn.c
+ * tcp connection class
+ */
+
+#include "etch_thread.h"
+#include "etch_tcp_connection.h"
+#include "etch_encoding.h"
+#include "etch_flexbuffer.h"
+#include "etch_log.h"
+#include "etch_objecttypes.h"
+
+static const char* LOG_CATEGORY = "etch_tcp_connection";
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+extern apr_thread_mutex_t* g_etch_main_pool_mutex;
+
+int etch_tcpconx_closex(etch_tcp_connection*, const int, const int);
+i_session* etch_tcpclient_get_session (void*);
+
+
+//extern char* ETCH_CONNECTION_LOGID;
+#define ETCH_SHUTDOWNSIGNALSIZE (sizeof(ETCH_SHUTDOWNSIGNAL)-1)
+
+unsigned connection_id_farm;
+
+#if(0)
+
+ TCPCONNECTION
+ | Socket, hostIP, port, delay, isKeepalive, isNoDelay
+ | buffersize, isAutoflush, trafficclass
+ | InputStream, OutputStream
+ | stop0(); openSocket(); setupSocket(); readSocket();
+ | close(); send(); flush(); shutdownInput(); shutdownOutput();
+ | remoteAddress(); fireData(); transportData();
+ |
+ - CONNECTION<SESSIONDATA>
+ | | Monitor status;
+ | | Connection(); started(); stopped(); exception();
+ | | run0(); localAddress(); translateHost();
+ | | openSocket(); setupSocket(); readSocket(); close();
+ | | transportQuery(); transportControl(); transportNotify();
+ | | fireUp(); fireDown();
+ | | void* getSession(); setSession(void*); waitUp(); waitDown();
+ | |
+ | - SESSION
+ | | sessionQuery(); sessionControl(); sessionNotify();
+ | |
+ | - RUNNER
+ | | | Thread thread;
+ | | | RunnerHandler handler;
+ | | | start0()
+ | | | stop0()
+ | | | run()
+ | | | run0()
+ | | | fireStarted()
+ | | | fireStopped()
+ | | | fireException()
+ | | - ABSTRACTSTARTABLE
+ | |
+ | - TRANSPORT<SESSIONDATA>
+ | | transportQuery(); transportControl(); transportNotify();
+ | |
+ | - RUNNERHANDLER interface
+ | started(); stopped(); exception();
+ |
+ - TRANSPORTDATA
+ | int transportData(to, buffer);
+ | int headerSize;
+ - TRANSPORT
+ transportQuery(); transportControl(); transportNotify();
+#endif
+
+
+
+
+/**
+ * etch_tcpconx_set_socket_options()
+ */
+int etch_tcpconx_set_socket_options(void* data)
+{
+ etch_tcp_connection *c = (etch_tcp_connection*)data;
+ int arc = 0, ecount = 0;
+ etch_connection_event_handler eventx;
+ etch_rawsocket* socket = c? c->cx.socket: NULL;
+ if (!socket) return -1;
+ eventx = c->cx.on_event;
+
+ /*
+ * APR_SO_DEBUG - turn on debugging information
+ * APR_SO_KEEPALIVE - keep connections active
+ * APR_SO_LINGER - lingers on close if data is present
+ * APR_SO_NONBLOCK - turns blocking on/off for socket
+ * when this option is enabled, use the APR_STATUS_IS_EAGAIN() macro
+ * to determine if a send or receive function could not transfer data
+ * without blocking.
+ * APR_SO_REUSEADDR - the rules used in validating addresses
+ * supplied to bind should allow reuse of local addresses.
+ * APR_SO_SNDBUF - set the send buffer size
+ * APR_SO_RCVBUF - set the receive buffer size
+ */
+
+ if (0 != (arc = apr_socket_opt_set(socket, APR_SO_KEEPALIVE, c->is_keepalive)))
+ ecount += eventx(c, ETCH_CONXEVT_SOCKOPTERR, arc, "keepalive");
+
+ if (0 != (arc = apr_socket_opt_set(socket, APR_SO_LINGER, c->linger)))
+ ecount += eventx(c, ETCH_CONXEVT_SOCKOPTERR, arc, "linger");
+
+ if (0 != (arc = apr_socket_opt_set(socket, APR_TCP_NODELAY, c->is_nodelay)))
+ ecount += eventx(c, ETCH_CONXEVT_SOCKOPTERR, arc, "nodelay");
+
+ /*
+ if (0 != (arc = apr_socket_opt_set(socket, APR_SO_NONBLOCK, FALSE)))
+ ecount += eventx(c, ETCH_CONXEVT_SOCKOPTERR, arc, "do not block");
+ */
+
+ /* timeout < 0 = block, 0 = never block, > 0 = block until timeout
+ if (0 != (arc = apr_socket_timeout_set(socket, -1)))
+ ecount += eventx(c, ETCH_CONXEVT_SOCKOPTERR, arc, "socket timeout");
+ */
+
+ return ecount == 0? 0: -1;
+}
+
+
+/**
+ * etch_tcpclient_on_data()
+ * tcp socket received data handler.
+ * @param cx the connection object.
+ * @param unused parameter not currently used.
+ * @param length number of bytes in the supplied data buffer.
+ * @param data the data as received via the socket wrapped in a flexbuffer.
+ * caller retains this memory.
+ * @remarks todo: if this remains the same as etch_tcpsvr_on_data, replace both
+ * methods with a etch_tcpconx_on_data() containing the same code.
+ */
+int etch_tcpclient_on_data (void* thisData, const int unused, int length, void* bufferData)
+{
+ etch_connection* cx = (etch_connection*)thisData;
+ etch_flexbuffer* data = (etch_flexbuffer*)bufferData;
+ int result = 0;
+ i_sessiondata* session = NULL;
+ etch_tcp_connection* tcpx = cx? (etch_tcp_connection*) cx->owner: NULL;
+ ETCH_ASSERT(is_etch_tcpconnection(tcpx));
+ ETCH_ASSERT(is_etch_flexbuffer(data));
+ session = tcpx->session;
+
+ /* send the data up the chain to be packetized. note that tcpx->session->thisx
+ * is the owner of the i_sessiondata* session, which is the next higher layer
+ * of the transport stack, which is ordinarily the packetizer.
+ */
+ if (-1 == (result = session->session_data (session->thisx, NULL, data)))
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "current %d bytes on connxn %d rejected\n", length, cx->conxid);
+ return result;
+}
+
+/*
+ * new_tcp_connection()
+ * etch_tcp_connection tcp client constructor
+ */
+etch_tcp_connection* new_tcp_connection(etch_url* url, void* resources, etch_rawsocket* socket)
+{
+ int result = -1, item = 0;
+ etch_tcp_connection* newcon = NULL;
+ if (!is_good_tcp_params (url, resources, socket)) return NULL;
+
+ newcon = (etch_tcp_connection*)new_object (sizeof(etch_tcp_connection), ETCHTYPEB_CONNECTION, CLASSID_TCP_CONNECTION);
+ ((etch_object*)newcon)->destroy = destroy_etch_tcp_connection;
+
+ do /* populate connection's transport and session interfaces */
+ { if (-1 == (result = init_etch_tcpconx_interfaces(newcon))) break;
+
+ if (-1 == (result = etch_init_connection (&newcon->cx, socket, newcon))) break;
+
+ newcon->cx.set_socket_options = etch_tcpconx_set_socket_options;
+ newcon->cx.on_event = etch_tcpconx_on_event; /* connection state handler */
+
+ if (socket)
+ newcon->cx.socket = socket;
+ else
+ {
+ // TODO: pool
+ etch_encoding_transcode_wchar(&newcon->cx.hostname, ETCH_ENCODING_UTF8, url->host, NULL);
+ newcon->cx.port = url->port;
+ etchurl_get_integer_term (url, ETCH_CONNECTION_RECONDELAY, &newcon->cx.delay);
+ }
+
+ /* set term default values - values for any terms not set here are zero */
+ newcon->is_nodelay = ETCH_CONNECTION_DEFNODELAY;
+ newcon->linger = ETCH_CONNECTION_DEFLINGERTIME;
+
+ /* set any terms which may have been supplied with URL */
+ etchurl_get_boolean_term(url, ETCH_CONNECTION_AUTOFLUSH, &newcon->is_autoflush);
+ etchurl_get_boolean_term(url, ETCH_CONNECTION_KEEPALIVE, &newcon->is_keepalive);
+ etchurl_get_boolean_term(url, ETCH_CONNECTION_NODELAY, &newcon->is_nodelay);
+ etchurl_get_integer_term(url, ETCH_CONNECTION_LINGERTIME, &newcon->linger);
+ etchurl_get_integer_term(url, ETCH_CONNECTION_TRAFCLASS, &newcon->traffic_class);
+ etchurl_get_integer_term(url, ETCH_CONNECTION_BUFSIZE, &item);
+ if (item > 0) newcon->cx.bufsize = item;
+ result = 0;
+
+ } while(0);
+
+ newcon->cx.on_data = etch_tcpclient_on_data;
+ newcon->cx.on_event(newcon, result? ETCH_CONXEVT_CREATERR: ETCH_CONXEVT_CREATED, 0, 0);
+
+ if (-1 == result)
+ { destroy_etch_tcp_connection(newcon);
+ return NULL;
+ }
+ else return newcon;
+}
+
+/**
+ * etch_tcpclient_set_session()
+ * i_transport::set_session() override
+ * @param session an i_sessiondata*. caller retains this object.
+ */
+void etch_tcpclient_set_session (void* data, void* newsession)
+{
+ etch_tcp_connection* thisx = (etch_tcp_connection*)data;
+ ETCH_ASSERT(is_etch_tcpconnection(thisx));
+ ETCH_ASSERT(is_etch_sessiondata(newsession));
+ if (thisx->is_session_owned){
+ etch_object_destroy(thisx->session);
+ thisx->session = NULL;
+ }
+ thisx->is_session_owned = FALSE;
+ thisx->session = newsession;
+}
+
+/**
+ * etch_tcpconx_transport_control()
+ * connection::i_transport::transport_control override.
+ * this is the base connection class' implementation of i_transport.
+ * this is java binding's Connection.transportControl(), and serves as the
+ * Transport part of the java TcpConnection TransportData.
+ * while tcp connection does implement i_transportdata, tcp connection's
+ * implementation of i_transport comes from its inheritance of connection,
+ * and its implementation of TransportData. since we do not separately implement
+ * the connection class, the i_transport methods are implemented here.
+ * @param control the event, sender relinquishes.
+ * @param value control value, sender relinquishes.
+ */
+int etch_tcpconx_transport_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_tcp_connection* thisx = (etch_tcp_connection*)data;
+ etch_connection* cx = NULL;
+ int result = 0, timeoutms = 0;
+ const int objclass = control? ((etch_object*)control)->class_id: 0;
+ const int is_client = is_etch_int32(value)? ((etch_int32*)value)->value: NULL;
+ ETCH_ASSERT(is_etch_tcpconnection(thisx));
+ cx = &thisx->cx;
+
+
+ switch(objclass)
+ {
+ case CLASSID_CONTROL_START:
+
+ result = etch_tcpconx_start (thisx);
+
+ if (is_client && 0 == result)
+ result = etch_tcpclient_start_listener (thisx);
+ break;
+
+ case CLASSID_CONTROL_START_WAITUP:
+
+ /* open the connection, and wait for completion. caller blocks by virtue
+ * of the fact that this is of course a function call, not a message handler.
+ * timeout is communicated to caller via result code 1 = ETCH_TIMEOUT.
+ * it is not clear why wait up is implemented here. since a tcp server
+ * implements transport interface itself, a server will never invoke this
+ * implementation. on the other hand, the requester of a client connection
+ * and the socket itself are the same thread, wait up therefore being
+ * meaningless since the socket open is known to be complete prior to
+ * invoking wait up.
+ */
+ if (0 == (result = etch_tcpconx_open (thisx, ETCH_CONX_NOT_RECONNECTING)))
+ { timeoutms = value? ((etch_int32*) value)->value: 0;
+ result = etchconx_wait_up (cx, timeoutms);
+ }
+ break;
+
+ case CLASSID_CONTROL_STOP:
+
+ if (is_client)
+ result = etch_tcpclient_stop_listener (thisx);
+ else
+ result = etch_tcpconx_close (thisx, ETCH_CONX_NO_LINGER);
+ break;
+
+ case CLASSID_CONTROL_STOP_WAITDOWN:
+
+ /* see comments above at CLASSID_CONTROL_START_WAITUP */
+ if (0 == (result = etch_tcpconx_close (thisx, ETCH_CONX_NO_LINGER)))
+ { timeoutms = value? ((etch_int32*) value)->value: 0;
+ result = etchconx_wait_down (cx, timeoutms);
+ }
+ break;
+ }
+
+ etch_object_destroy(control);
+ etch_object_destroy(value);
+ return result;
+}
+
+/**
+ * etch_tcpconx_transport_notify()
+ * i_transport::transport_notify override.
+ * @param evt, caller relinquishes.
+ */
+int etch_tcpconx_transport_notify (void* data, etch_event* evt)
+{
+ etch_tcp_connection* thisx = (etch_tcp_connection*)data;
+ ETCH_ASSERT(is_etch_tcpconnection(thisx));
+ etch_object_destroy(evt);
+ return 0; /* nothing to do */
+}
+
+
+
+/**
+ * etch_tcpconx_transport_query()
+ * i_transport::transport_query override.
+ * @param query, caller relinquishes.
+ */
+etch_object* etch_tcpconx_transport_query (void* data, etch_query* query)
+{
+ etch_tcp_connection* thisx = (etch_tcp_connection*)data;
+ int result = 0;
+ etch_object* resultobj = NULL;
+ etch_connection* cx = NULL;
+ const int timeoutms = query? query->value: 0;
+ const int objclass = query? ((etch_object*)query)->class_id: 0;
+ ETCH_ASSERT(is_etch_tcpconnection(thisx));
+ cx = &thisx->cx;
+
+ switch(objclass)
+ {
+ case CLASSID_QUERY_LOCALADDR:
+ /* TODO return wrapped local address */
+ break;
+
+ case CLASSID_QUERY_REMOTEADDR:
+ /* TODO return wrapped remote address */
+ break;
+
+ case CLASSID_QUERY_WAITUP:
+ result = etchconx_wait_up(cx, timeoutms);
+ break;
+
+ case CLASSID_QUERY_WAITDOWN:
+ result = etchconx_wait_down(cx, timeoutms);
+ break;
+ }
+
+ etch_object_destroy(query);
+ return resultobj;
+}
+
+
+/*
+ * etch_tcpclient_sendex()
+ * send data with specified timeout
+ */
+int etch_tcpclient_sendex (etch_tcp_connection *conx, unsigned char* buf,
+ const size_t totallen, const int timeout_ms, int* rc)
+{
+ int arc = 0, is_eod = 0;
+ int64 existing_timeout_us = 0;
+ apr_size_t datalen = 0, totalsent = 0, remaining = totallen;
+ etch_connection *cx = conx? &conx->cx: NULL;
+ etch_rawsocket* socket = cx? cx->socket: NULL;
+ if (!socket) return -1;
+ cx->on_event(conx, ETCH_CONXEVT_SENDING, 0, 0);
+
+ if (timeout_ms)
+ { apr_socket_timeout_get(cx->socket, &existing_timeout_us);
+ apr_socket_timeout_set(cx->socket, timeout_ms * 1000);
+ }
+
+ /* note socket currently blocking write with no timeout */
+
+ while(cx->is_started && !is_eod && remaining > 0)
+ {
+ datalen = totallen;
+
+ is_eod = (APR_EOF == (arc = apr_socket_send(socket, (char*)(buf + totalsent), &datalen)));
+
+ totalsent += datalen; remaining -= datalen;
+
+ if (arc != 0 && !is_eod)
+ { cx->on_event(conx, ETCH_CONXEVT_SENDERR, arc, 0);
+ break;
+ }
+
+ cx->on_event(conx, ETCH_CONXEVT_SENT, is_eod, (char*)datalen);
+ }
+
+ if (timeout_ms) /* restore socket timeout property */
+ apr_socket_timeout_set(cx->socket, existing_timeout_us);
+
+ cx->on_event(conx, ETCH_CONXEVT_SENDEND, (int)totalsent, 0);
+ if (rc) *rc = arc;
+ return remaining > 0? -1: 0;
+}
+
+/*
+ * etch_tcpclient_send()
+ */
+int etch_tcpclient_send (etch_tcp_connection *conx, unsigned char* buf, const size_t totallen, int* rc)
+{
+ return etch_tcpclient_sendex(conx, buf, totallen, 0, rc);
+}
+
+
+/*
+ * etch_tcpconx_transport_data()
+ * etch_tcp_connection::i_transportdata::transport_data
+ * @param whoto caller retains
+ * @param fbuf caller retains
+ */
+int etch_tcpconx_transport_data (void* data, etch_who* whoto, etch_flexbuffer* fbuf)
+{
+ etch_tcp_connection* thisx = (etch_tcp_connection*)data;
+ int result = 0, apr_rc = 0;
+ ETCH_ASSERT(is_etch_tcpconnection(thisx));
+
+ result = etch_tcpclient_send (thisx, fbuf->buf, fbuf->datalen, &apr_rc);
+
+ etch_flexbuf_reset(fbuf);
+ return result;
+}
+
+/*
+ * init_etch_tcpcon_interfaces()
+ * populate transport and placeholder session interfaces to tcp connection.
+ */
+int init_etch_tcpconx_interfaces (etch_tcp_connection* tcpx)
+{
+ i_transport* itransport = NULL;
+ ETCH_ASSERT(is_etch_tcpconnection(tcpx));
+ if (tcpx->itd) return 0; /* already initialized */
+
+ itransport = new_transport_interface_ex (tcpx,
+ etch_tcpconx_transport_control,
+ etch_tcpconx_transport_notify,
+ etch_tcpconx_transport_query,
+ etch_tcpclient_get_session,
+ etch_tcpclient_set_session);
+
+ tcpx->itd = new_transportdata_interface (tcpx,
+ (void*)etch_tcpconx_transport_data, itransport); /* itd now owns itransport */
+
+ /* establish placeholder session interface which is expected
+ * to be replaced by the connection host (e.g. packetizer) */
+ tcpx->session = new_sessiondata_interface (tcpx, NULL, NULL);
+ tcpx->is_session_owned = TRUE;
+
+ return 0;
+}
+
+
+/**
+ * etch_tcpconx_start()
+ * start means open. generally we would come through here with an accepted socket,
+ * in which case it is currently marked already open and we will return success.
+ * @return 0 success, -1 failure.
+ */
+int etch_tcpconx_start (etch_tcp_connection *conx)
+{
+ etch_connection* cx = conx? &conx->cx: NULL;
+ ETCH_ASSERT(cx);
+ cx->on_event (conx, ETCH_CONXEVT_STARTING, 0, 0);
+ if (cx->is_started) return 0;
+
+ return etch_tcpconx_open (conx, ETCH_CONX_NOT_RECONNECTING);
+}
+
+
+/**
+ * etch_tcpconx_open()
+ * open connection to server based on host name/port set at construction.
+ * @note we have omitted reconnect logic for now, pending logic to detect
+ * listen socket down and initiate reconnect.
+ * @return 0 success, -1 failure (already open, hostname or socket error, etc)
+ */
+int etch_tcpconx_open(etch_tcp_connection *conx, const int is_reconnect)
+{
+ int result = -1, arc = 0, attempt = 0, is_already_open = TRUE;
+ apr_status_t apr_status;
+ apr_interval_time_t apr_timeout;
+ etch_connection* cx = &conx->cx;
+ etch_connection_event_handler eventx = cx->on_event;
+ eventx(conx, ETCH_CONXEVT_OPENING, 0, 0);
+
+ do
+ { if (cx->is_started) break;
+ is_already_open = FALSE;
+
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ arc = apr_sockaddr_info_get(&cx->sockdata, cx->hostname, ETCH_DEFAULT_SOCKET_FAMILY, cx->port, 0, g_etch_main_pool);
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+ if (0 != arc) {
+ eventx(conx, ETCH_CONXEVT_OPENERR, 4, (void*)(size_t)arc);
+ break;
+ }
+
+ if (!cx->socket)
+ {
+ if (0 != (arc = new_tcpsocket (&cx->socket, cx->aprpool)))
+ { eventx(conx, ETCH_CONXEVT_OPENERR, 3, (void*)(size_t)arc);
+ break;
+ }
+
+ /* set socket options here: NONBLOCK, TIMEOUT */
+ }
+
+ apr_status = apr_socket_timeout_get(cx->socket, &apr_timeout);
+ if(apr_status != APR_SUCCESS){
+ char buffer[1024];
+ apr_strerror(apr_status, buffer, sizeof(buffer));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not get socket options: %s\n", buffer);
+ }
+
+ apr_status = apr_socket_timeout_set(cx->socket, 5 * APR_USEC_PER_SEC);
+ if(apr_status != APR_SUCCESS){
+ char buffer[1024];
+ apr_strerror(apr_status, buffer, sizeof(buffer));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not set socket options: %s\n", buffer);
+ }
+
+ while(attempt++ < ETCH_CONNECTION_DEFRETRYATTEMPTS+1)
+ { /* possibly todo: configure number of retry attempts */
+ /* open socket */
+
+ apr_status = apr_socket_connect (cx->socket, cx->sockdata);
+ if(apr_status != APR_SUCCESS){
+ char buffer[1024];
+ apr_strerror(apr_status, buffer, sizeof(buffer));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not connect to server reson: %s\n", buffer);
+ } else {
+ cx->is_started = TRUE;
+ break;
+ }
+
+ cx->on_event(conx, ETCH_CONXEVT_OPENERR, 2, (void*)(size_t)arc);
+ etch_sleep(ETCH_CONNECTION_DEFRETRYDELAYMS);
+ }
+
+ apr_status = apr_socket_timeout_set(cx->socket, apr_timeout);
+ if(apr_status != APR_SUCCESS){
+ char buffer[1024];
+ apr_strerror(apr_status, buffer, sizeof(buffer));
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not set socket options: %s\n", buffer);
+ }
+
+ } while(0);
+
+ if (cx->is_started && !is_already_open) result = 0;
+ eventx(conx, result? ETCH_CONXEVT_OPENERR: ETCH_CONXEVT_OPENED, 0, 0);
+
+ if (cx->wait_up) {
+
+ /* java binding inserts a flow of control discontinuity here by assigning
+ * the responsibility for notifying the session of the new connection state
+ * to the separate thread it calls the todo manager (see jave fireUp()),
+ * the purpose being to avoid a race condition by delaying the state change
+ * until after return from this method. we may eventually have to implement
+ * something like this, but as of this writing we are not doing so. the fact
+ * that we change state and then unblock as two separate actions here may
+ * well preclude the need for such a discontinuity here.
+ */
+
+ /* notify session that connection is up (see comment above) */
+ /* fyi: conx->session->thisx is packetizer */
+ if (0 == result) {
+ conx->session->session_notify (conx->session->thisx, new_etch_event(0, ETCHEVT_SESSION_UP));
+ }
+
+ /* unblock the thread waiting for connection to come up */
+ /* todo permit unblock with something other than UP, when result != 0 */
+ etch_wait_set(cx->wait_up, ETCH_CONXEVT_UP);
+ }
+
+ return result;
+}
+
+/*
+ * etch_tcpcconx_close()
+ * close tcp connection
+ */
+int etch_tcpconx_close(etch_tcp_connection* conx, const int is_linger)
+{
+ return etch_tcpconx_closex(conx, is_linger, FALSE);
+}
+
+
+/*
+ * etch_tcpcconx_closex()
+ * close tcp connection
+ * @param is_linger whether to set the socket to linger.
+ * @param is_dtor true only if this call is from the tcp connection destructor.
+ * @return 0 success, -1 failure.
+ */
+int etch_tcpconx_closex(etch_tcp_connection* conx, const int is_linger, const int is_dtor)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ int result = 0, arc = 0, is_locked = 0, is_teardown = 0, is_logged = 0;
+ etch_connection* cx = 0;
+ if (NULL == conx) return -1;
+ cx = &conx->cx;
+ is_teardown = is_dtor && !cx->is_started;
+
+ do {
+
+ if (is_teardown) break;
+ cx->on_event(conx, ETCH_CONXEVT_CLOSING, 0, 0);
+
+ if (cx->socket)
+ {
+ if (cx->is_closing || !cx->is_started)
+ { cx->on_event(conx, ETCH_CONXEVT_CLOSERR, 1, 0);
+ is_logged = TRUE;
+ break;
+ }
+ }
+
+ status = etch_mutex_trylock(cx->mutex);
+ if(status != ETCH_SUCCESS) {
+ cx->on_event(conx, ETCH_CONXEVT_CLOSERR, 2, 0);
+ is_logged = TRUE;
+ break;
+ }
+
+ is_locked = TRUE;
+ cx->is_closing = TRUE;
+ cx->is_started = FALSE;
+ if (NULL == cx->socket) break; /* never opened */
+
+ if (is_linger)
+ apr_socket_opt_set(cx->socket, APR_SO_LINGER, conx->linger);
+ /* else flush(); shutdown_output(); */
+
+ if (0 != (arc = apr_socket_close(cx->socket))) {
+ cx->on_event(conx, ETCH_CONXEVT_CLOSERR, 3, (void*)(size_t)arc);
+ result = -1;
+ break;
+ }
+
+ // join thread on client receiver thread
+ if(conx->rcvlxr != NULL) {
+ etch_join(conx->rcvlxr->thread);
+ }
+
+ cx->socket = NULL;
+ cx->is_closing = FALSE;
+
+ /* anything else we may need to do on close here */
+
+ } while(0);
+
+ if (is_locked) {
+ etch_mutex_unlock(cx->mutex);
+ }
+
+ if (!is_teardown && !is_logged)
+ cx->on_event(conx, result? ETCH_CONXEVT_CLOSERR: ETCH_CONXEVT_CLOSED, 0, 0);
+
+ if (cx->wait_down) {
+ /* if another thread is blocking on this condition variable,
+ * we set the condition to DOWN, and UNBLOCK all waiters. */
+ etch_wait_set(cx->wait_down, ETCH_CONXEVT_DOWN);
+ }
+
+ return result;
+}
+
+
+/*
+ * destroy_etch_tcp_connection()
+ * etch_tcp_connection destructor
+ */
+int destroy_etch_tcp_connection(void* thisx)
+{
+ etch_tcp_connection* tcpx = (etch_tcp_connection*)thisx;
+ if (NULL == tcpx) return -1;
+ tcpx->cx.on_event(tcpx, ETCH_CONXEVT_DESTROYING, 0, 0);
+
+ etch_tcpconx_closex (tcpx, FALSE, TRUE); /* close if open */
+
+ if (!is_etchobj_static_content(tcpx)) {
+
+ /* free listener if any */
+ etch_object_destroy(tcpx->rcvlxr);
+ tcpx->rcvlxr = NULL;
+
+ /* free mem owned by tcpx */
+ etch_destroy_connection (&tcpx->cx);
+
+ /* free session interface */
+ if (tcpx->is_session_owned) {
+ etch_object_destroy(tcpx->session);
+ tcpx->session = NULL;
+ }
+
+ /* free transport interface */
+ etch_object_destroy(tcpx->itd);
+ tcpx->itd = NULL;
+ }
+
+ tcpx->cx.on_event(tcpx, ETCH_CONXEVT_DESTROYED, 0, 0);
+ return destroy_objectex((etch_object*)tcpx);
+}
+
+
+/*
+ * new_tcpsocket()
+ */
+int new_tcpsocket (apr_socket_t** outsock, apr_pool_t* mempool)
+{
+ int rv = 0;
+ apr_thread_mutex_lock(g_etch_main_pool_mutex);
+ rv = apr_socket_create (outsock, APR_INET, SOCK_STREAM, APR_PROTO_TCP, g_etch_main_pool);
+ apr_thread_mutex_unlock(g_etch_main_pool_mutex);
+
+ return rv;
+}
+
+
+
+
+
+/*
+ * etch_tcpclient_receive()
+ * receive data on socket
+ * returns length received or -1
+ */
+int etch_tcpclient_receive (etch_tcp_connection *tcpx, unsigned char* buf, const size_t buflen, int* rc)
+{
+ return etch_tcpclient_receivex (tcpx, buf, buflen, 0, rc);
+}
+
+
+/*
+ * etch_tcpclient_receivex()
+ * receive data on socket with specified timeout, into specified character buffer.
+ * @return number of bytes received on success; otherwise -2 (ETCH_OTHER_END_CLOSED)
+ * if peer closed, or -1 if error.
+ */
+int etch_tcpclient_receivex (etch_tcp_connection *tcpx, unsigned char* buf, const size_t buflen, const int timeout_ms, int* rc)
+{
+ int result = 0, arc = 0, is_eod = 0, eventid = 0;
+ int64 existing_timeout_us = 0;
+ apr_size_t datalen = 0;
+ etch_connection *cx = tcpx? &tcpx->cx: NULL;
+ if (NULL == cx) return -1;
+ cx->on_event(tcpx, ETCH_CONXEVT_RECEIVING, 0, 0);
+
+ if (timeout_ms) {
+ apr_socket_timeout_get(cx->socket, &existing_timeout_us);
+ apr_socket_timeout_set(cx->socket, timeout_ms * 1000);
+ }
+
+ datalen = buflen; /* BLOCK on receive data here */
+
+ arc = apr_socket_recv (cx->socket, (char*)buf, &datalen);
+ is_eod = arc == APR_EOF;
+
+ if (arc && !is_eod) {
+ switch(arc) {
+ case APR_OTHER_END_CLOSED:
+ eventid = ETCH_CONXEVT_PEERCLOSED;
+ result = ETCH_OTHER_END_CLOSED;
+ break;
+ case APR_THIS_END_CLOSED:
+ eventid = ETCH_CONXEVT_CONXCLOSED;
+ result = ETCH_THIS_END_CLOSED;
+ break;
+ default:
+ eventid = ETCH_CONXEVT_RECEIVERR;
+ result = -1;
+ }
+ cx->on_event (tcpx, eventid, arc, 0);
+
+ }
+ else
+ if (0 == datalen)
+ { cx->on_event(tcpx, ETCH_CONXEVT_PEERCLOSED, 0, 0);
+ result = ETCH_OTHER_END_CLOSED;
+ }
+ else /* check for signal to shut down server */
+ if (datalen == ETCH_SHUTDOWNSIGNALSIZE
+ && 0 == memcmp(buf, ETCH_SHUTDOWNSIGNAL, ETCH_SHUTDOWNSIGNALSIZE))
+ { cx->on_event(tcpx, ETCH_CONXEVT_SHUTDOWN, 0, 0);
+ result = ETCH_SHUTDOWN_NOTIFIED;
+ }
+ else
+ { cx->on_event (tcpx, ETCH_CONXEVT_RECEIVED, is_eod, (char*)datalen);
+ if (-1 != result) result = (int) datalen; /* return bytecount */
+ }
+
+ if (timeout_ms) /* restore socket timeout property */
+ apr_socket_timeout_set(cx->socket, existing_timeout_us);
+
+ if (rc) *rc = arc;
+ return result;
+}
+
+
+static etch_status_t etch_tcp_client_cleanup(void* p)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_status_t status = ETCH_SUCCESS;
+ etch_tcp_client* client = p;
+
+ status = etch_object_destroy(client->thread);
+ // TODO: check status
+ client->thread = NULL;
+
+ status = destroy_objectex((etch_object*)client);
+ // TODO: check status
+
+ rv = status;
+
+ return rv;
+}
+
+
+/**
+ * destroy_etch_tcp_client()
+ * tcp client (tcp connection read listener) destructor.
+ */
+int destroy_etch_tcp_client(void* data)
+{
+ etch_tcp_client* thisx = (etch_tcp_client*)data;
+ etch_status_t rv = ETCH_SUCCESS;
+ rv = etch_tcp_client_cleanup(thisx);
+ return rv;
+}
+
+
+/**
+ * etch_tcpclient_listenerproc()
+ * tcp socket receive thread procedure.
+ */
+static void etch_tcp_client_receiver_proc(void* data)
+{
+ etch_thread_params* params = (etch_thread_params*)data;
+ int result = 0, arc = 0;
+ etch_tcp_connection* tcpx = (etch_tcp_connection*) params->data;
+ etch_connection* cx = &tcpx->cx;
+ const int thread_id = params->etch_thread_id;
+ const int blen = cx->bufsize? cx->bufsize: ETCH_CONX_DEFAULT_BUFSIZE;
+ //params->data->threas = params->threadob
+ etch_flexbuffer* fbuf = new_flexbuffer(blen);
+ cx->on_event(tcpx, ETCH_CONXEVT_RCVPUMP_START, 0, 0);
+
+ while(cx->is_started)
+ {
+ etch_flexbuf_clear(fbuf); /* for debugging otherwise unnecessary */
+ cx->on_event(tcpx, ETCH_CONXEVT_RCVPUMP_RECEIVING, thread_id, 0);
+
+ /* receive data from tcp socket into buffer owned by flexbuffer.
+ * note that if this receive were to stop blocking, for example
+ * if the peer went down without it being detected here, we would
+ * see unfettered looping of this listener procedure. BLOCK.
+ */
+ result = etch_tcpclient_receive (tcpx, fbuf->buf, blen, &arc);
+
+ switch(result)
+ {
+ case ETCH_THIS_END_CLOSED: case ETCH_OTHER_END_CLOSED:
+ /* a socket is down so close connection and exit thread */
+ cx->is_started = FALSE; /* this is new: exit thread now */
+ result = 0; /* was break here but next line catches it*/
+ }
+
+ if (!cx->is_started) break; /* client shutdown */
+
+ if (result < 0)
+ { cx->on_event(tcpx, ETCH_CONXEVT_RCVPUMP_ERR, arc, 0);
+ break;
+ }
+
+ etch_flexbuffer_reset_to (fbuf, result); /* received (result) bytes */
+
+ if (result > 0) /* forward (result) bytes to data handler for packetization */
+ cx->on_data (cx, 0, result, fbuf);
+ }
+
+ tcpx->session->session_notify (tcpx->session->thisx, new_etch_event(0, ETCHEVT_SESSION_DOWN));
+
+ cx->on_event(tcpx, ETCH_CONXEVT_RCVPUMP_STOP, result, (void*) (size_t) thread_id);
+ etch_object_destroy(fbuf);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "leaving listener thread ...\n");
+}
+
+/**
+ * new_tcp_client()
+ * tcp client (tcp connection read listener) constructor.
+ * this class is an afterthought so it is backwards, the connection hosting
+ * the client class. maybe we'll change it later to move to client and server
+ * connection class symmetry. however this works. when a client connection
+ * needs a read listener it hosts and owns one of these.
+ * @param tcpx the tcp connection which is the client's receive listener.
+ */
+etch_tcp_client* new_tcp_client (etch_tcp_connection* tcpx)
+{
+ etch_tcp_client* newclient = NULL;
+
+ newclient = (etch_tcp_client*)new_object(sizeof(etch_tcp_client), ETCHTYPEB_TCPCLIENT, CLASSID_TCP_CLIENT);
+
+ ((etch_object*)newclient)->destroy = destroy_etch_tcp_client;
+ newclient->cxlisten = tcpx; /* client's receive listener is tcpx */
+
+ newclient->thread = new_thread(etch_tcp_client_receiver_proc, tcpx);
+ if(newclient->thread == NULL) {
+ tcpx->cx.on_event (tcpx, ETCH_CONXEVT_STARTERR, 1, 0);
+ etch_object_destroy(newclient);
+ newclient = NULL;
+ }
+ newclient->thread->start(newclient->thread);
+
+
+ //status = etch_thread_start(newclient->thread);
+ // TODO: check error
+
+
+ /* the threadpool acts as the server's thread manager. it creates threads
+ * on request and destroys them at thread exit. */
+ //newclient->threadpool = new_threadpool (ETCH_THREADPOOLTYPE_FREE, 1);
+
+ /* data passed to threads will be either this object, or tcp connection
+ * objects. here we configure thread mgr to not free these at thread exit */
+ //newclient->threadpool->is_free_data = FALSE;
+ //newclient->threadpool->is_data_etchobject = TRUE;
+ //newclient->is_started = TRUE;
+
+ /* start the receive thread on the local thread manager */
+ //if (NULL == newclient->threadpool->run(newclient->threadpool, etch_tcp_client_receiver_proc, tcpx)) {
+ // tcpx->cx.on_event (tcpx, ETCH_CONXEVT_STARTERR, 1, 0);
+ // newclient->destroy(newclient);
+ // newclient = NULL;
+ //}
+
+ return newclient;
+}
+
+
+/**
+ * etch_tcpclient_start_listener
+ * start a receive listener thread on the client connection
+ */
+int etch_tcpclient_start_listener (etch_tcp_connection *tcpx)
+{
+ etch_connection *cx = tcpx? &tcpx->cx: NULL;
+ //if (NULL == cx || NULL != tcpx->rcvlxr) return -1;
+ if (NULL == cx)
+ return -1;
+ else if (NULL != tcpx->rcvlxr)
+ {
+ etch_object_destroy(tcpx->rcvlxr);
+ tcpx->rcvlxr = NULL;
+ }
+
+ tcpx->rcvlxr = new_tcp_client (tcpx);
+
+ return NULL == tcpx->rcvlxr? -1: 0;
+}
+
+
+/**
+ * etch_tcpclient_stop_listener
+ * stop the receive listener thread on the client connection
+ */
+int etch_tcpclient_stop_listener (etch_tcp_connection *tcpx)
+{
+ int result = 0;
+ etch_tcp_client* tcpclient = NULL;
+ etch_tcp_connection* clientconx = NULL;
+ etch_connection *cx = tcpx? &tcpx->cx: NULL;
+ if (NULL == cx || NULL == tcpx->rcvlxr) return -1;
+
+ tcpclient = tcpx->rcvlxr;
+ clientconx = tcpclient->cxlisten;
+
+ tcpclient->is_started = FALSE;
+
+ result = etch_tcpconx_close (clientconx, FALSE);
+
+ // aprrc = apr_socket_send (clientconx->cx.socket, ETCH_SHUTDOWNSIGNAL, &datalen);
+
+ // result = etch_tcpclient_send (tcpx, ETCH_SHUTDOWNSIGNAL, ETCH_SHUTDOWNSIGNALSIZE, &aprrc);
+
+ return result;
+ //return aprrc == 0? 0: -1;
+}
+
+
+
+
+
+
+/*
+ * etch_tcpclient_stop()
+ */
+int etch_tcpclient_stop (etch_tcp_connection *conx)
+{
+ return etch_tcpconx_close(conx, 0);
+}
+
+
+/* - - - - - - - - - - - - - - - - -
+ * tcp client :: i_transportdata
+ * - - - - - - - - - - - - - - - - -
+ */
+
+
+
+/* - - - - - - - - - - - - - - -
+ * tcpclient :: i_transport
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etch_tcpclient_get_session
+ * i_transport::get_session implementation
+ */
+i_session* etch_tcpclient_get_session (void* data)
+{
+ etch_tcp_connection* thisx = (etch_tcp_connection*)data;
+ ETCH_ASSERT(is_etch_tcpconnection(thisx));
+ return (i_session*)thisx->session;
+}
+
+
+
+
+
+
+
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_tcp_server.c b/binding-c/runtime/c/src/main/transport/etch_tcp_server.c
new file mode 100644
index 0000000..d6e3f21
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_tcp_server.c
@@ -0,0 +1,1100 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_connection.c
+ * connection client and server classes - tcp, udp
+ */
+
+#include "etch_thread.h"
+#include "etch_tcp_server.h"
+#include "etch_encoding.h"
+#include "etch_flexbuffer.h"
+#include "etch_session_message.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+#include "etch_runtime.h"
+#include "etch_config.h"
+
+static const char* LOG_CATEGORY = "etch_tcpserver";
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+extern apr_thread_mutex_t* g_etch_main_pool_mutex;
+
+// TODO: module wide
+// - etch_tcpserver_static_create
+// - etch_tcpserver_static_destroy
+// - synchronize tcp servicer id
+// - synchronize tco connection id
+// - separating extern and static functions inside module
+
+
+char* ETCHTCPS = "TCPS";
+
+#define ETCH_CONNECTION_DEFLINGERTIME 30
+#define ETCH_CONNECTION_DEFNODELAY TRUE
+#define ETCH_CONNECTION_DEFRETRYDELAYMS 1000
+
+unsigned next_etch_tcpserver_id();
+unsigned next_etch_connection_id();
+
+etch_tcp_connection* new_accepted_tcp_connection
+ (char* host, const int port, etch_rawsocket*);
+
+int etch_tcpsvr_dummy_connection(etch_tcp_connection*);
+int etch_deftcplistener_on_event(etch_tcp_server*, etch_tcp_connection*, const int, int, void*);
+
+/* transport interface */
+int etch_tcpsvr_transport_control(void*, etch_event*, etch_object*);
+int etch_tcpsvr_transport_notify (void*, etch_event*);
+etch_object* etch_tcpsvr_transport_query (void*, etch_query*);
+i_session* etch_tcpsvr_get_session(void*);
+
+/* session listener interface stubs */
+etch_object* etch_tcpsvr_stub_session_query (void*, etch_query*);
+
+
+#if(0)
+
+ TCPSERVER
+ | Socket, hostIP, port, delay, isKeepalive, isNoDelay
+ | buffersize, isAutoflush, trafficclass
+ | InputStream, OutputStream
+ | stop0(); openSocket(); setupSocket(); readSocket();
+ | close(); send(); flush(); shutdownInput(); shutdownOutput();
+ | remoteAddress(); fireData(); transportData();
+ |
+ - CONNECTION<SESSIONLISTENER>
+ | | sessionListener session;
+ | | sessionAccepted(socket);
+ | | SESSION
+ | | sessionQuery(); sessionControl(); sessionNotify();
+ | |
+ | | Monitor status;
+ | | Connection(); started(); stopped(); exception();
+ | | run0(); localAddress(); translateHost();
+ | | openSocket(); setupSocket(); readSocket(); close();
+ | | transportQuery(); transportControl(); transportNotify();
+ | | fireUp(); fireDown();
+ | | waitUp(); waitDown();
+ | |
+ | - RUNNER
+ | | | Thread thread;
+ | | | RunnerHandler handler;
+ | | | start0()
+ | | | stop0()
+ | | | run()
+ | | | run0()
+ | | | fireStarted()
+ | | | fireStopped()
+ | | | fireException()
+ | | - ABSTRACTSTARTABLE
+ | |
+ | - TRANSPORT<SESSIONDATA>
+ | | transportQuery(); transportControl(); transportNotify();
+ | |
+ | - RUNNERHANDLER interface
+ | started(); stopped(); exception();
+ |
+ - TRANSPORT<SESSIONLISTENER>
+ | SessionListener getSession(); setSession(SessionListener);
+ | transportQuery(); transportControl(); transportNotify();
+
+#endif
+
+
+
+/* - - - - - - - - - - - - - - - - -
+ * constructors / destructors
+ * - - - - - - - - - - - - - - - - -
+ */
+
+
+/**
+ * etch_tcpsvr_on_data()
+ * tcp socket received data handler.
+ * @param cx the connection object.
+ * @param uu parameter currently unused.
+ * @param length number of bytes in the supplied data buffer.
+ * @param data the data as received via the socket wrapped in a flexbuffer.
+ * caller retains this memory.
+ */
+int etch_tcpsvr_on_data (void* connectionData, const int uu, int length, void* bufferData)
+{
+ etch_connection* cx = (etch_connection*)connectionData;
+ etch_flexbuffer* data = (etch_flexbuffer*)bufferData;
+ int result = 0;
+ i_sessiondata* session = NULL;
+ etch_tcp_connection* tcpx = cx? (etch_tcp_connection*) cx->owner: NULL;
+ ETCH_ASSERT(is_etch_tcpconnection(tcpx));
+ ETCH_ASSERT(is_etch_flexbuffer(data));
+ ETCH_ASSERT(is_etch_sessiondata(tcpx->session));
+ session = tcpx->session;
+
+ /* send the data up the chain to be packetized. note that tcpx->session->thisx
+ * is the owner of the i_sessiondata* session, which is the next higher layer
+ * of the transport stack, which is ordinarily the packetizer.
+ */
+ if (-1 == (result = session->session_data (session->thisx, NULL, data)))
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_WARN, "%d bytes via connxn %d discarded\n",
+ length, cx->conxid);
+ return result;
+}
+
+
+/*
+ * new_accepted_tcp_connection()
+ * etch_tcp_connection constructor for use with an accepted socket
+ * receive thread exit must delete this object
+ */
+etch_tcp_connection* new_accepted_tcp_connection(char* host, const int port, etch_rawsocket* accepted_socket)
+{
+ int result = 0;
+ etch_tcp_connection* newcon = NULL;
+ if (!host || !accepted_socket) return NULL;
+
+ newcon = (etch_tcp_connection*) new_object (sizeof(etch_tcp_connection), ETCHTYPEB_CONNECTION, CLASSID_TCP_CONNECTION);
+ ((etch_object*)newcon)->destroy = destroy_etch_tcp_connection;
+
+ /* 1/1/09 need to verify this is correct - unit test failed since it expected
+ * tcpconx.session to be non-null - while new_tcp_connection() initialized
+ * these interfaces, the new_accepted_connection() did not - watch this spot
+ * in case it turns out that the transport and session interfaces now plugged
+ * in here are incorrect. fyi unit test now passes but must re-vet runtime.
+ */
+ if (0 != init_etch_tcpconx_interfaces (newcon)) return NULL; /* 1/1/09 */
+
+ etch_init_connection (&newcon->cx, accepted_socket, newcon);
+
+ newcon->cx.socket = accepted_socket;
+ newcon->cx.hostname = etch_malloc(strlen(host)+1, ETCHTYPEB_BYTES);
+ strcpy(newcon->cx.hostname, host);
+ newcon->cx.port = port;
+ newcon->is_nodelay = ETCH_CONNECTION_DEFNODELAY;
+ newcon->linger = ETCH_CONNECTION_DEFLINGERTIME;
+ newcon->cx.on_event = etch_tcpconx_on_event; /* connection state handler */
+ #ifndef IS_ETCH_NO_SESSIONDATA /* dead-end data for testing */
+ newcon->cx.on_data = etch_tcpsvr_on_data; /* received data handler */
+ #endif
+
+ if (0 == result)
+ newcon->cx.on_event (newcon, ETCH_CONXEVT_CREATED, (int) (size_t) accepted_socket, 0);
+ else
+ { newcon->cx.on_event (newcon, ETCH_CONXEVT_CREATERR, 0, 0);
+ destroy_etch_tcp_connection(newcon);
+ newcon = NULL;
+ }
+
+ return newcon;
+}
+
+
+/*
+ * destroy_etch_tcp_server()
+ * etch_tcp_server destructor
+ */
+int destroy_etch_tcp_server (void* data)
+{
+ etch_tcp_server* svr = (etch_tcp_server*)data;
+ int result = 0, serverid = 0;
+ if (NULL == svr) return -1;
+ serverid = svr->listener_id;
+ svr->on_event(svr, 0, ETCH_CONXEVT_DESTROYING, 0, 0);
+
+ etch_tcpsvr_close(svr); /* close if open */
+
+ if (!is_etchobj_static_content(svr))
+ destroy_etch_tcp_connection(svr->cxlisten);
+
+ if (svr->session && svr->is_session_owned)
+ ((etch_object*)svr->session)->destroy (svr->session);
+
+ if (svr->itransport)
+ etch_free(svr->itransport);
+
+ if (svr->threadpool && svr->is_threadpool_owned)
+ etch_object_destroy(svr->threadpool);
+
+ etch_object_destroy(svr->thread);
+ svr->thread = NULL;
+
+ etch_mutex_destroy(svr->client_connections_mutex);
+ svr->client_connections_mutex = NULL;
+
+ etch_linked_list_destroy(svr->client_connections);
+ svr->client_connections = NULL;
+
+ svr->on_event(svr, 0, ETCH_CONXEVT_DESTROYED, 0, 0);
+
+ result = destroy_objectex((etch_object*)svr);
+ return result;
+}
+
+
+/**
+ * etch_tcpsvr_set_session()
+ * i_transport::set_session() override
+ * @param session an i_sessionlistener*. caller owns this object.
+ */
+void etch_tcpsvr_set_session(void* data, void* sessionData)
+{
+ etch_tcp_server* thisx = (etch_tcp_server*)data;
+ i_sessionlistener* session = (i_sessionlistener*)sessionData;
+ ETCH_ASSERT(is_etch_tcpserver(thisx));
+ ETCH_ASSERT(is_etch_sessionlxr(session));
+
+ thisx->is_session_owned = FALSE; /* internal caller will re-set */
+
+ if (thisx->session) /* replacing? */
+ { ETCH_ASSERT(is_etch_sessionlxr(thisx->session));
+ etch_object_destroy(thisx->session);
+ }
+
+ thisx->session = session;
+ thisx->isession = session->isession;
+ thisx->session_control = session->session_control;
+ thisx->session_notify = session->session_notify;
+ thisx->session_query = session->session_query;
+ thisx->on_session_accepted = session->session_accepted;
+}
+
+int etch_tcpsvr_stub_session_control (void* obj, etch_event* evt, etch_object* v)
+{
+ return -1;
+}
+
+int etch_tcpsvr_stub_session_notify (void* obj, etch_event* event)
+{
+ return -1;
+}
+
+/*
+ * etch_tcpsvr_stub_on_session_accepted()
+ * i_sessionlistener::session_accepted default implementation
+ * @param thisx
+ * @param socket presumably an etch_socket wrapper. caller relinquishes.
+ */
+int etch_tcpsvr_stub_on_session_accepted(void* thisx, void* socket)
+{
+
+ return -1;
+}
+
+
+/**
+ * new_tcp_server()
+ * etch_tcp_server constructor
+ */
+etch_tcp_server* new_tcp_server(etch_url* url, etch_threadpool* mp, etch_threadpool* sp, etch_resources* resxmap, i_sessionlistener* insession)
+{
+ etch_tcp_server* svr = NULL;
+
+ // check parameters
+ if (NULL == url)
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "Invalid argument url is null\n");
+ return NULL;
+ }
+
+ svr = (etch_tcp_server*) new_object(sizeof(etch_tcp_server), ETCHTYPEB_TCPSERVER, CLASSID_TCP_LISTENER);
+ svr->listener_id = next_etch_tcpserver_id();
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "creating tcp server %d ...\n", svr->listener_id);
+
+ ((etch_object*)svr)->destroy = destroy_etch_tcp_server;
+ ((etch_object*)svr)->clone = clone_null;
+ svr->on_event = etch_deftcplistener_on_event;
+ svr->on_data = etch_defconx_on_data;
+ svr->resxmap = resxmap; /* not owned, can be null */
+ svr->threadpool = mp; /* currently always passed in */
+ svr->subpool = sp; /* currently always passed in */
+ svr->url = url;
+ svr->thread = NULL;
+
+ etch_linked_list_create(&svr->client_connections, 0);
+
+ etch_mutex_create(&svr->client_connections_mutex , ETCH_MUTEX_NESTED, NULL);
+
+ /* - - - - - - - - - - - - - - -
+ * transport (i_transport)
+ * - - - - - - - - - - - - - - -
+ */
+ svr->itransport = new_transport_interface(svr,
+ (etch_transport_control) etch_tcpsvr_transport_control,
+ (etch_transport_notify) etch_tcpsvr_transport_notify,
+ (etch_transport_query) etch_tcpsvr_transport_query);
+
+ svr->transport_control = etch_tcpsvr_transport_control;
+ svr->transport_notify = etch_tcpsvr_transport_notify;
+ svr->transport_query = etch_tcpsvr_transport_query;
+
+ svr->get_session = etch_tcpsvr_get_session;
+ svr->set_session = etch_tcpsvr_set_session;
+
+
+ /* - - - - - - - - - - - - - - -
+ * session (i_sessionlistener)
+ * - - - - - - - - - - - - - - -
+ */
+
+ if (insession)
+ {
+ svr->set_session(svr, insession);
+ svr->is_session_owned = FALSE;
+ }
+ else
+ { i_session* isession = new_session_interface(svr,
+ etch_tcpsvr_stub_session_control,
+ etch_tcpsvr_stub_session_notify,
+ etch_tcpsvr_stub_session_query);
+
+ i_sessionlistener* newsession = new_sessionlistener_interface(svr,
+ etch_tcpsvr_stub_on_session_accepted,
+ isession);
+
+ svr->set_session(svr, newsession);
+ svr->is_session_owned = TRUE;
+ }
+
+
+ /* - - - - - - - - - - - - - - -
+ * etch_tcp_server continued
+ * - - - - - - - - - - - - - - -
+ */
+
+ /* create the listener's tcp connection */
+ svr->cxlisten = new_tcp_connection (url, NULL, NULL);
+
+ if (svr->cxlisten)
+ {
+ svr->cxlisten->cx.listener = (etch_object*) svr;
+ svr->on_event(svr, 0, ETCH_CONXEVT_CREATED, 0, 0);
+ }
+ else
+ { svr->on_event(svr, 0, ETCH_CONXEVT_CREATERR, 0, 0);
+ destroy_etch_tcp_server(svr);
+ svr = NULL;
+ }
+
+ if (svr)
+ ETCH_LOG(ETCHTCPS, ETCH_LOG_DEBUG, "tcp server %d created\n", svr->listener_id);
+ else
+ ETCH_LOG(ETCHTCPS, ETCH_LOG_ERROR, "could not create tcp server\n");
+
+ return svr;
+}
+
+/* - - - - - - - - - - - - - - - - -
+ * tcp server methods
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etch_tcpsvr_open()
+ * open the server accept listener socket.
+ */
+int etch_tcpsvr_open (etch_tcp_server *svr, const int is_reconnect)
+{
+ int result = -1, is_new_socket = 0, arc = 0, attempt = 0;
+ etch_tcp_connection* tcpx = NULL;
+ etch_connection* cx = NULL;
+ ETCH_ASSERT(is_etch_tcpserver(svr));
+ tcpx = svr->cxlisten;
+ cx = &tcpx->cx;
+ if (svr->state != ETCH_TCPSERVER_STATE_CLOSED) return 0;
+ svr->on_event(svr, tcpx, ETCH_CONXEVT_OPENING, 0, 0);
+ is_new_socket = cx->socket == NULL;
+
+ if (is_reconnect)
+ if (!cx->socket || !cx->hostname || !*cx->hostname || !cx->delay)
+ return svr->on_event(svr, tcpx, ETCH_CONXEVT_OPENERR, 0, 0);
+ do
+ { if (0 != (arc = apr_sockaddr_info_get(&cx->sockdata, cx->hostname,
+ ETCH_DEFAULT_SOCKET_FAMILY, cx->port, 0, cx->aprpool)))
+ { svr->on_event(svr, tcpx, ETCH_CONXEVT_OPENERR, 4, (void*)(size_t)arc);
+ break;
+ }
+
+ if (is_new_socket) /* not reconnecting, create a socket */
+ {
+ if (0 != (arc = new_tcpsocket (&cx->socket,cx->aprpool)))
+ { svr->on_event(svr, tcpx, ETCH_CONXEVT_OPENERR, 3, (void*)(size_t)arc);
+ break;
+ }
+
+ /* set socket options here: NONBLOCK, TIMEOUT */
+ }
+
+ apr_socket_opt_set(cx->socket, APR_SO_REUSEADDR, 1);
+
+ while(attempt++ < ETCH_CONNECTION_DEFRETRYATTEMPTS+1)
+ {
+ if (is_reconnect || attempt > 0)
+ etch_sleep(cx->delay);
+
+ if (0 != (arc = apr_socket_bind(cx->socket, cx->sockdata)))
+ { svr->on_event(svr, tcpx, ETCH_CONXEVT_OPENERR, 5, (void*)(size_t)arc);
+ continue;
+ }
+
+ if (0 == (arc = apr_socket_listen(cx->socket, svr->backlog)))
+ { cx->is_started = TRUE;
+ svr->on_event(svr, tcpx, ETCH_CONXEVT_LISTENED, cx->conxid, 0);
+ break;
+ }
+
+ svr->on_event(svr, tcpx, ETCH_CONXEVT_OPENERR, 6, (void*)(size_t)arc);
+ }
+
+ } while(0);
+
+ if (cx->is_started)
+ { result = 0; /* stopped means no longer closed but not yet started */
+ svr->state = ETCH_TCPSERVER_STATE_STOPPED;
+ }
+ else
+ if (is_new_socket)
+ cx->socket = NULL;
+
+ svr->on_event(svr, tcpx,
+ result? ETCH_CONXEVT_OPENERR: ETCH_CONXEVT_OPENED, 0, 0);
+
+ return result;
+}
+
+
+/*
+ * etch_tcpsvr_close()
+ * close server socket
+ */
+int etch_tcpsvr_close (etch_tcp_server* lxr)
+{
+ int result = 0;
+ etch_connection* cx = NULL;
+ ETCH_ASSERT(is_etch_tcpserver(lxr));
+
+ if (lxr->state < ETCH_TCPSERVER_STATE_STOPPED)
+ return -1;
+
+ if (lxr->state > ETCH_TCPSERVER_STATE_STOPPED)
+ result = etch_tcpsvr_stop(lxr); /* SVR BREAK 007 */
+
+ if (lxr->state != ETCH_TCPSERVER_STATE_STOPPED)
+ return -1;
+
+ cx = &lxr->cxlisten->cx;
+ lxr->state = ETCH_TCPSERVER_STATE_CLOSING;
+ lxr->on_event(lxr, 0, ETCH_CONXEVT_CLOSING, 0, 0);
+
+ result = etch_tcpconx_close(lxr->cxlisten, 0); /* close listen socket */
+
+ lxr->state = ETCH_TCPSERVER_STATE_CLOSED;
+ lxr->on_event(lxr, 0, result? ETCH_CONXEVT_CLOSERR: ETCH_CONXEVT_CLOSED, 0, 0);
+
+ if (cx->wait_down)
+ {
+ /* if another thread is blocking on this condition variable, we set
+ * the condition to DOWN, which is presumably what the other thread
+ * will unblock on.
+ */
+ etch_wait_set(cx->wait_down, ETCH_CONXEVT_DOWN);
+ }
+
+ return result;
+}
+
+
+/**
+ * etch_tcp_listener_client_connection_proc
+ * tcp receive thread procedure for accepted session connections.
+ * this is the client session lifetime pump. it runs in its own thread, which
+ * exists until the peer socket is closed, the session requests closure of the
+ * session or server, or a socket error is detected.
+ */
+static void etch_tcp_listener_client_connection_proc(void* data)
+{
+ etch_thread_params* params = (etch_thread_params*)data;
+ unsigned char* buf = NULL;
+ int result = 0, arc = 0;
+ etch_flexbuffer* fbuf = NULL;
+ const int thread_id = params->etch_thread_id;
+ int is_shutdown_request = FALSE;
+ int is_other_end_closed = FALSE, is_this_end_closed = FALSE;
+ etch_tcp_connection* accx = (etch_tcp_connection*) params->data;
+ etch_connection* cx = &accx->cx;
+ etch_tcp_server* svr =(etch_tcp_server*) cx->listener;
+ const int session_id = cx->conxid;
+ const int blen = cx->bufsize? cx->bufsize: ETCH_CONX_DEFAULT_BUFSIZE;
+
+ ETCH_ASSERT(is_etch_tcpconnection(accx));
+ cx->on_event = etch_tcpconx_on_event;
+
+
+ svr->on_event(svr, accx, ETCH_CONXEVT_RCVPUMP_START, 0, 0);
+ fbuf = new_flexbuffer(blen);
+ buf = etch_flexbuf_get_buffer(fbuf);
+
+ /* we are using blocking sockets. fyi if we switch to non-blocking mode
+ * in the future, the APR_STATUS_IS_EAGAIN(rc) macro is the test we want
+ * to make on the read or write result to determine if the socket was not
+ * ready to read or write - that macro tests multiple apr result codes.
+ */
+
+ accx->session->session_notify (accx->session->thisx, new_etch_event(0, ETCHEVT_SESSION_UP));
+
+ /* receive pump -- blocking read */
+ while (svr->state == ETCH_TCPSERVER_STATE_STARTED && cx->is_started) {
+ memset(buf, 0, blen); /* nice for debugging but otherwise unnecessary */
+ svr->on_event(svr, accx, ETCH_CONXEVT_RCVPUMP_RECEIVING, thread_id, 0);
+
+ /* receive data from tcp socket into buffer owned by flexbuffer.
+ * note that if this receive were to stop blocking, for example if the
+ * peer went down without it being detected here, we would see serious
+ * slowdown begin here due to unfettered looping of the listener proc.
+ */
+ result = etch_tcpclient_receive (accx, buf, blen, &arc); /* block */
+
+ /* if result is >= 0, it is the number of bytes received */
+
+ if (svr->state != ETCH_TCPSERVER_STATE_STARTED || !cx->is_started){
+ //in this case the svr stops the conx
+ params->is_own_data = 0;
+ break; /* if server stopped, exit */
+ }
+
+ if (APR_OTHER_END_CLOSED == arc || ETCH_OTHER_END_CLOSED == result)
+ { is_other_end_closed = TRUE; /* if peer down, exit */
+ break;
+ }
+
+ if (APR_THIS_END_CLOSED == arc)
+ { is_this_end_closed = TRUE; /* if this end down, exit */
+ break;
+ }
+
+ if (ETCH_SHUTDOWN_NOTIFIED == result)
+ { is_shutdown_request = TRUE; /* client sent server shutdown request */
+ svr->is_started = FALSE; /* (as opposed to client stop request) */
+ break;
+ }
+
+ if (result < 0) /* if socket error, exit */
+ { svr->on_event(svr, svr->cxlisten, ETCH_CONXEVT_RCVPUMP_ERR, arc, 0);
+ break;
+ }
+
+ etch_flexbuf_set_length(fbuf, result);
+ etch_flexbuf_set_index (fbuf, 0);
+
+ if (result > 0) /* forward (result) bytes to data handler for packetization */
+ result = cx->on_data (cx, 0, result, fbuf);
+ }
+
+ /* at this point the client receive loop has exited, most likely due to
+ * socket closed on either end, but could also be due to a socket error.
+ */
+
+ etch_mutex_lock(svr->client_connections_mutex);
+ svr->connections--; // TODO: connection count can also be accessed by client_connection list
+ etch_linked_list_remove(svr->client_connections, accx);
+ etch_mutex_unlock(svr->client_connections_mutex);
+
+ if (is_this_end_closed || is_other_end_closed || is_shutdown_request) result = 0;
+ svr->on_event(svr, accx, ETCH_CONXEVT_RCVPUMP_STOP, result, (void*)(size_t)thread_id);
+
+ accx->session->session_notify (accx->session->thisx, new_etch_event(0, ETCHEVT_SESSION_DOWN));
+
+ if (is_shutdown_request)
+ { /* if this receive caught a server shutdown request from a client, do not destroy
+ * this session, but rather message back to the main listener to shut itself down.
+ * the main thread is blocking on the main listener thread, and will iterate and
+ * destroy all the server's sessions once the listener thread unblocks.
+ */
+ i_sessionlistener* mainlistener = svr->session;
+
+ const int resultx = mainlistener->transport_control (mainlistener->thisx, new_etch_event(CLASSID_CONTROL_STOP_WAITDOWN, ETCH_INFWAIT), NULL);
+
+ /* we will not return here until after main listener has been destroyed */
+ result = resultx; /* this extra local is simply a breakpoint target */
+ }
+ else
+ { /* this code is executed when a session is terminates normally, either due to
+ * peer connection closing, or client stop request. this code is not executed
+ * if the client is forced down from the main thread.
+ */
+ ETCH_LOG(ETCHTCPS, ETCH_LOG_DEBUG, "session %d begin teardown ...\n", session_id);
+
+ /* tear down client session not including accx */
+ etch_object_destroy(cx->session);
+ cx->session = NULL;
+
+ /* destroy client connection object */
+ etch_object_destroy(accx);
+ accx = NULL;
+
+ ETCH_LOG(ETCHTCPS, ETCH_LOG_DEBUG, "session %d destroyed\n", session_id);
+ }
+
+ etch_object_destroy(fbuf); /* we now exit the client session receive thread */
+ ETCH_LOG(ETCHTCPS, ETCH_LOG_INFO, "client session %d ends\n", session_id);
+ ETCH_LOG(ETCHTCPS, ETCH_LOG_DEBUG, "session %d thread exit ...\n", session_id);
+}
+
+
+
+
+/**
+ * etch_tcpsvr_acceptproc()
+ * accept pump
+ */
+static void etch_tcp_listener_accept_proc(void* data)
+{
+ etch_thread_params* params = (etch_thread_params*)data;
+ int result = 0, arc = 0;
+ etch_tcp_server* listener = (etch_tcp_server*) params->data;
+ const int thread_id = params->etch_thread_id;
+ etch_tcp_connection* newx = 0;
+ etch_tcp_connection* tcpx = listener->cxlisten;
+ etch_connection* cx = &tcpx->cx;
+ etch_rawsocket* listensock = cx->socket;
+ etch_rawsocket* newsock = NULL;
+ etch_thread* newthread = NULL;
+ apr_pool_t* temp_pool = NULL;
+ int max_number_of_connections = 0;
+ etch_config_t* config;
+
+ ETCH_ASSERT(is_etch_tcpserver(listener));
+
+ while (listener->is_started) {
+ listener->on_event(listener, tcpx, ETCH_CONXEVT_ACCEPTING, 0, 0);
+
+ /* each accepted connection gets its own apr subpool, which is freed when
+ * the connection is destroyed. this accounts for apr memory specific to
+ * the connection which is not explicitly freed, such as apr_socket_t */
+ ETCH_ASSERT(g_etch_main_pool != NULL);
+
+ apr_pool_create(&temp_pool, NULL);
+ //printf("4 creating apr pool %p\n",temp_pool);
+
+ /* socket block here for a client connection request */
+ if (0 != (arc = apr_socket_accept (&newsock, listensock, temp_pool))) {
+ listener->on_event(listener, tcpx, ETCH_CONXEVT_ACCEPTERR, 0,(void*)(size_t)arc);
+ /* if server shutdown, no error */
+ if (listener->is_started) {
+ result = -1;
+ }
+ /* TODO: catch other conditions in which nonzero return not error */
+ break;
+ }
+
+ if (!listener->is_started) {
+ /* Even when we disconnect, close the newly created socket. So we don't have to wait
+ for the timeout. */
+ if (newsock != NULL)
+ apr_socket_close(newsock);
+ break; /* server shutdown */
+ }
+ ETCH_LOG(ETCHTCPS, ETCH_LOG_DEBUG, "connect request for socket %x\n", (size_t)newsock);
+
+ etch_runtime_get_config(&config);
+ ETCH_ASSERT(config);
+ etch_config_get_property_int(config, "etch.maxconnections", &max_number_of_connections);
+ if(max_number_of_connections == 0) {
+ max_number_of_connections = 40;
+ }
+
+ if(listener->connections >= max_number_of_connections){
+ ETCH_LOG(ETCHTCPS, ETCH_LOG_DEBUG, "max number of connections (%d) reached, not accepting more connection requests\n", listener->connections);
+ apr_socket_close(newsock);
+ continue;
+ }
+
+ /* create the new accepted connection object. note that this object
+ * will be freed when its listener thread exits. SVR BREAK 004
+ */
+ newx = new_accepted_tcp_connection (cx->hostname, cx->port, newsock);
+
+ newx->cx.listener = (etch_object*)listener;
+ newx->cx.is_started = TRUE;
+
+ listener->on_event (listener, newx, ETCH_CONXEVT_ACCEPTED, 0, 0);
+
+ /* 1/4/09 permit socket data handler to be overridden, e.g. by a unit test */
+ if (listener->on_data && listener->on_data != etch_defconx_on_data)
+ newx->cx.on_data = listener->on_data;
+
+ /* TODO use the sessionlistener interface to call back to the accepted handler.
+ * we temporarily plugged it in directly to the tcp server object in new_etch_listener().
+ */
+ /* e.g. transport.tcpxfact_session_accepted */
+ if (listener->on_session_accepted) {
+ listener->on_session_accepted (listener->session, newx); /* SVR BREAK 005 */
+ }
+
+ /* on return from on_session_accepted, the connection cx.session is a reference
+ * to the etch_session* client session data object. the thread we start below
+ * will call the destructor on this object when it exits, in order to destroy
+ * the client session objects such as delivery service, stub, etc.
+ */
+
+ etch_mutex_lock(listener->client_connections_mutex);
+ listener->connections++;
+ etch_linked_list_add(listener->client_connections, newx);
+ etch_mutex_unlock(listener->client_connections_mutex);
+
+ /* run a read listener for the client connection, on a thread from the client pool,
+ * via etch_threadpool_run_freethread, etch_thread_start, etch_tcpserver_listenerproc.
+ * SVR BREAK 006
+ */
+ if (NULL == ( newx->cx.thread = newthread = listener->threadpool->run (listener->subpool, etch_tcp_listener_client_connection_proc, newx)))
+ {
+ listener->on_event (listener, newx, ETCH_CONXEVT_STARTERR, 1, 0);
+ etch_object_destroy(newx);
+ newx = NULL; /* todo newx should cx.session.destroy() */
+ result = -1;
+ break;
+ }
+
+ /* the new client session is now executing on its own thread.
+ * loop back to continue listening for client connection requests.
+ */
+
+ }
+
+ if(0 == result) {
+ listener->on_event(listener, tcpx, ETCH_CONXEVT_ACCEPTPUMPEXIT, thread_id, 0);
+ } else {
+ listener->on_event(listener, tcpx, ETCH_CONXEVT_ACCEPTPUMPEXITERR, 0, 0);
+ }
+}
+
+
+
+/**
+ * etch_tcpsvr_start()
+ * start accepting connections
+ */
+int etch_tcpsvr_start (etch_tcp_server* tcpsvr)
+{
+ int result = 0;
+ etch_tcp_connection* tcpx = tcpsvr->cxlisten;
+ etch_connection* cx = &tcpx->cx;
+
+ if (tcpsvr->state != ETCH_TCPSERVER_STATE_STOPPED)
+ return tcpsvr->on_event(tcpsvr, tcpx, ETCH_CONXEVT_STARTERR, 0, 0);
+
+ /* the threadpool acts as the server's thread manager. it creates threads
+ * on request and destroys them at thread exit. the main pool is always
+ * present here in the runtime, but could be null for unit tests. */
+ if (tcpsvr->threadpool == NULL) /* currently only null for unit tests */
+ { tcpsvr->threadpool = new_threadpool (ETCH_THREADPOOLTYPE_FREE, 0);
+ tcpsvr->is_threadpool_owned = TRUE;
+ }
+
+ /* data passed to threads will be either this object, or accepted connection
+ * objects. configure thread manager to not free this data at thread exit. */
+ tcpsvr->threadpool->is_free_data = FALSE;
+ tcpsvr->threadpool->is_data_etchobject = TRUE;
+
+ tcpsvr->state = ETCH_TCPSERVER_STATE_STARTED;
+ tcpsvr->is_started = TRUE;
+ tcpsvr->on_event(tcpsvr, tcpx, ETCH_CONXEVT_STARTING, 0, 0);
+
+ tcpsvr->thread = new_thread(etch_tcp_listener_accept_proc, tcpsvr);
+ if(tcpsvr->thread == NULL) {
+ // TODO: error handling thread could not be started
+ tcpsvr->on_event (tcpsvr, tcpx, ETCH_CONXEVT_STARTERR, 1, 0);
+ result = -1;
+ }
+ tcpsvr->thread->start(tcpsvr->thread);
+ // TODO: error logging
+
+ /* start the accept thread on the main thread manager. SVR BREAK 003 */
+ // TODO: run this as default thread
+ //if (NULL == tcpsvr->threadpool->run (tcpsvr->threadpool, etch_tcp_listerner_proc, tcpsvr))
+ //{
+ // tcpsvr->on_event (tcpsvr, tcpx, ETCH_CONXEVT_STARTERR, 1, 0);
+ // result = -1;
+ //}
+
+ tcpsvr->on_event(tcpsvr, tcpx, result? ETCH_CONXEVT_STARTERR: ETCH_CONXEVT_STARTED, 0, 0);
+
+ if (cx->wait_up)
+ {
+ /* if another thread is blocking on this condition variable, we set the wait
+ * variable to UP, which is presumably what the waiting thread will unblock on. */
+ //ETCH_ASSERT(is_etch_wait(cx->waiter));
+ etch_wait_set(cx->wait_up, ETCH_CONXEVT_UP);
+ }
+
+ return result;
+}
+
+
+/**
+ * etch_tcpsvr_stop()
+ * stop accepting connections and shut down the accept listener.
+ */
+int etch_tcpsvr_stop (etch_tcp_server* server)
+{
+ int result = 0;
+ etch_tcp_connection* tcpx = NULL;
+ etch_tcp_connection* client_con = NULL;
+ int connection_count = 0;
+
+ ETCH_ASSERT(is_etch_tcpserver(server));
+ tcpx = server? server->cxlisten: NULL;
+ if (!tcpx) return -1;
+
+ if (server->state < ETCH_TCPSERVER_STATE_STARTED)
+ return server->on_event(server, tcpx, ETCH_CONXEVT_STOPERR, 0, 0);
+
+ server->is_started = FALSE; /* pump threads conditional */
+ server->state = ETCH_TCPSERVER_STATE_STOPPING;
+ server->on_event(server, tcpx, ETCH_CONXEVT_STOPPING, 0, 0);
+
+ /* unblock the accept thread so it will recognize that it should exit */
+ result = etch_tcpsvr_dummy_connection (tcpx);
+
+ // wait to server thread finished
+ etch_join(server->thread);
+
+ //etch_sleep(ACCEPTWAITMS); /* pause here avoids accept thread hang */
+
+ etch_mutex_lock(server->client_connections_mutex);
+ connection_count = server->connections;
+ ETCH_ASSERT(connection_count == etch_linked_list_count(server->client_connections));
+ for(;connection_count > 0; connection_count--){
+ etch_linked_list_get(server->client_connections, 0, (void*)&client_con);
+ ETCH_ASSERT(client_con);
+ result = etch_tcpconx_close(client_con,0);
+ }
+ etch_mutex_unlock(server->client_connections_mutex);
+
+ /* BLOCK here until all threads exited */
+ result = threadpool_waitfor_all (server->threadpool, TRUE);
+
+ server->state = ETCH_TCPSERVER_STATE_STOPPED;
+ server->on_event(server, tcpx, ETCH_CONXEVT_STOPPED, 0, 0);
+ return result;
+}
+
+
+/**
+ * next_etch_tcpserver_id()
+ * return a unique ID used to identify a server instance
+ */
+unsigned next_etch_tcpserver_id()
+{
+ do { apr_atomic_inc32 ((volatile apr_uint32_t*) &tcpserver_id_farm);
+ } while(tcpserver_id_farm == 0);
+
+ return tcpserver_id_farm;
+}
+
+
+/**
+ * etch_tcpsvr_dummy_connection()
+ * create a dummy client connection so as to unblock the accept thread,
+ * in order that it can then recognize that it should exit, and do so.
+ * note that an invalid server address such as 0.0.0.0 will cause the connection
+ * attempt to fail, whereas it will not have caused the accept to fail. in such
+ * a case, the accept thread is left hanging, and a subsequent crash on service
+ * exit is likely. TODO both ends should validate IP addresses. etch_url does not.
+ */
+int etch_tcpsvr_dummy_connection (etch_tcp_connection* tcpx)
+{
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_socket_t* dummy = 0;
+ apr_sockaddr_t* addr = 0;
+ int attempts = 0;
+ etch_connection* cx = &tcpx->cx;
+ const int MAXATTEMPTS = 8, DELAY_BETWEEN_ATTEMPTS_MS = 100;
+
+ apr_status = new_tcpsocket(&dummy, cx->aprpool);
+ if(apr_status != APR_SUCCESS) {
+ cx->on_event(tcpx, ETCH_CONXEVT_OPENERR, 3, (void*)(size_t)apr_status);
+ }
+ else {
+ apr_status = apr_sockaddr_info_get(&addr, "127.0.0.1", APR_UNSPEC, cx->sockdata->port, 0, cx->aprpool);
+ if(apr_status != APR_SUCCESS) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not get addr info.");
+ }
+
+ while (1)
+ {
+ apr_status = apr_socket_connect (dummy, addr);
+ if(apr_status == APR_SUCCESS)
+ break;
+
+ if (++attempts > MAXATTEMPTS)
+ {
+ cx->on_event (tcpx, ETCH_CONXEVT_OPENERR, 2, (void*)(size_t)apr_status);
+ break;
+ }
+ else
+ etch_sleep(DELAY_BETWEEN_ATTEMPTS_MS);
+ }
+ }
+ return apr_status ? -1 : 0;
+}
+
+
+/* - - - - - - - - - - - - - - - - -
+ * tcp server :: i_sessionlistener
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * pointers to these funtions are copied from the i_sessionlistener implementation
+ * at set_session() time. these stub implementations are provided as placeholders.
+ */
+
+
+
+
+
+
+
+etch_object* etch_tcpsvr_stub_session_query (void* obj, etch_query* query)
+{
+ return NULL;
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * tcpserver :: i_transport
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etch_tcpsvr_get_session
+ * i_transport::get_session implementation.
+ * returns the i_session, whose thisx is a etch_session_listener*
+ */
+i_session* etch_tcpsvr_get_session(void* data)
+{
+ etch_tcp_server* thisx = (etch_tcp_server*)data;
+ ETCH_ASSERT(is_etch_tcpserver(thisx));
+ return thisx->isession;
+}
+
+
+
+
+/**
+ * etch_tcpsvr_transport_control()
+ * i_transport::transport_control override.
+ * @param control event, sender relinquishes.
+ * @param value control value, sender relinquishes.
+ */
+int etch_tcpsvr_transport_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_tcp_server* thisx = (etch_tcp_server*)data;
+ int result = 0;
+ etch_connection* cx = NULL;
+ const int timeoutms = value? ((etch_int32*) value)->value: 0;
+ const int objclass = control? ((etch_object*)control)->class_id: 0;
+ ETCH_ASSERT(is_etch_tcpserver(thisx));
+ cx = &thisx->cxlisten->cx;
+
+ switch(objclass)
+ {
+ case CLASSID_CONTROL_START:
+
+ if (0 == (result = etch_tcpsvr_open(thisx, ETCH_CONX_NOT_RECONNECTING)))
+ result = etch_tcpsvr_start(thisx);
+
+ break;
+
+ case CLASSID_CONTROL_START_WAITUP:
+
+ /* point to the condition variable on the waiter. this is a semikludge;
+ * however we need to have a target for the up state before we do the
+ * waitup, since the connect will complete before we get around to waitup,
+ * and it needs to be able to mark state as up. previously the state
+ * variable cond_var was not set until the wait_up was invoked. in the
+ * current design the cond_var is nulled out after a wait, in order to
+ * reset wait state to not waiting, so we need to ensure it is populated
+ * in advance of any need to set a wait condition to some state prior to
+ * actually waiting.
+ * TODO: etch_wait was changed remove this
+ */
+ //etchconx_init_waitstate(cx);
+
+ /* open the connection, and wait for completion. caller blocks here.
+ * timeout is indicated via result code 1 = ETCH_TIMEOUT. SVR BREAK 002
+ */
+ if (0 == (result = etch_tcpsvr_open(thisx, ETCH_CONX_NOT_RECONNECTING)))
+ if (0 == (result = etch_tcpsvr_start(thisx)))
+ result = etchconx_wait_up(cx, timeoutms);
+ break;
+
+ case CLASSID_CONTROL_STOP:
+
+ result = etch_tcpsvr_close(thisx);
+ break;
+
+ case CLASSID_CONTROL_STOP_WAITDOWN:
+ /* note all comments above at case CLASSID_CONTROL_START_WAITUP */
+ //etchconx_init_waitstate(cx);
+
+ if (0 == (result = etch_tcpsvr_close(thisx)))
+ result = etchconx_wait_down(cx, timeoutms);
+
+ break;
+ }
+
+ if (control)
+ etch_object_destroy(control);
+ if (value)
+ etch_object_destroy(value);
+ return result;
+}
+
+
+/**
+ * etch_tcpsvr_transport_notify()
+ * i_transport::transport_notify override.
+ * @param evt, caller relinquishes.
+ */
+int etch_tcpsvr_transport_notify (void* data, etch_event* evt)
+{
+ etch_tcp_server* thisx = (etch_tcp_server*)data;
+ ETCH_ASSERT(is_etch_tcpserver(thisx));
+ etch_object_destroy(evt);
+ return 0; /* nothing to do */
+}
+
+
+/**
+ * etch_tcpsvr_transport_query()
+ * i_transport::transport_query override.
+ * @param query, caller relinquishes.
+ */
+etch_object* etch_tcpsvr_transport_query (void* data, etch_query* query)
+{
+ etch_tcp_server* thisx = (etch_tcp_server*)data;
+ ETCH_ASSERT(is_etch_tcpserver(thisx));
+ /* todo is this right? */
+ return thisx->itransport->transport_query(thisx->itransport, query);
+}
+
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_tdformat.c b/binding-c/runtime/c/src/main/transport/etch_tdformat.c
new file mode 100644
index 0000000..cef73e2
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_tdformat.c
@@ -0,0 +1,120 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_tdformat.c
+ * a factory used to abstract tagged data handler constructors
+ */
+#include "etch_tdformat.h"
+#include "etch_objecttypes.h"
+#include <wchar.h>
+
+void etchtdf_init();
+int etchtdf_name_to_id(wchar_t*);
+tagged_data_input* etchtdf_defnewtdi (etch_value_factory*);
+tagged_data_output* etchtdf_defnewtdo (etch_value_factory*);
+
+const wchar_t* tdfname_binary = L"binary";
+const wchar_t* tdfname_xml = L"xml";
+
+
+/*
+ * tagdata_format_factory()
+ * @param format_name name of the tagged data format, e.g., L"binary",
+ * caller retains ownership of this string.
+ * @return a format factory (interface to tagged data constructors) for specified format.
+ * caller assumes ownership of this object.
+ */
+tagdata_format_factory* get_format_factory(wchar_t* format_name)
+{
+ tagdata_format_factory* ff = (tagdata_format_factory*) new_object
+ (sizeof(tagdata_format_factory), ETCHTYPEB_FORMATFACT, CLASSID_FORMATFACT);
+
+ ff->new_tagdata_input = etchtdf_defnewtdi;
+ ff->new_tagdata_output = etchtdf_defnewtdo;
+
+ switch(etchtdf_name_to_id(format_name))
+ { case ETCH_TAGDATA_FORMAT_BINARY:
+ ((etch_object*)ff)->class_id = CLASSID_FORMATFACT_BINARY;
+ ff->new_tagdata_input = new_binary_tdi;
+ ff->new_tagdata_output = new_binary_tdo;
+ break;
+ case ETCH_TAGDATA_FORMAT_XML:
+ ((etch_object*)ff)->class_id = CLASSID_FORMATFACT_XML;
+ /* overrides will be plugged in here at such time as we have xml tdi/tdo */
+ break;
+ default: /* name not recognized */
+ break;
+ }
+
+ return ff;
+}
+
+
+/*
+ * etchtdf_init()
+ * static data lazy initializer
+ */
+void etchtdf_init()
+{
+ if (is_tdf_initialized) return;
+ tdformat_names[ETCH_TAGDATA_FORMAT_BINARY] = tdfname_binary;
+ tdformat_names[ETCH_TAGDATA_FORMAT_XML] = tdfname_xml;
+ is_tdf_initialized = TRUE;
+}
+
+
+/*
+ * etchtdf_name_to_id()
+ * translate name of tagdata format to format ID (index)
+ */
+int etchtdf_name_to_id(wchar_t* format_name)
+{
+ int i = 0;
+ etchtdf_init();
+ if (NULL == format_name) return -1;
+
+ for(; i < ETCH_TAGDATA_FORMAT_COUNT; i++)
+ if (0 == wcscmp(format_name, tdformat_names[i]))
+ return i;
+
+ return -1;
+}
+
+
+/*
+ * tdf_defnewtdi()
+ * default virtual new_tagdata_input()
+ */
+tagged_data_input* etchtdf_defnewtdi (etch_value_factory* vf)
+{
+ return NULL;
+}
+
+
+/*
+ * tdf_defnewtdi()
+ * default virtual new_tagdata_output()
+ */
+tagged_data_output* etchtdf_defnewtdo (etch_value_factory* vf)
+{
+ return NULL;
+}
+
+
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_transport.c b/binding-c/runtime/c/src/main/transport/etch_transport.c
new file mode 100644
index 0000000..f28bb47
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_transport.c
@@ -0,0 +1,1574 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_transport.c
+ * common transport functionality
+ */
+
+#include "etch.h"
+#include "etch_runtime.h"
+#include "etch_plain_mailbox_manager.h"
+#include "etch_svcobj_masks.h"
+#include "etch_plain_mailbox.h"
+#include "etch_transport.h"
+#include "etch_packetizer.h"
+#include "etch_messagizer.h"
+#include "etch_tcp_server.h"
+#include "etch_tcp_connection.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+static const char* LOG_CATEGORY = "etch_transport";
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+typedef etch_plainmailboxmgr etch_mailbox_manager;
+
+
+int tcpdelsvc_init (etch_tcp_delivery_service*);
+int destroy_delivery_service_interface (void*);
+int destroy_delivery_service_via_interface(void*);
+int destroy_tcp_delivery_service(void*);
+int tcpdelsvc_begincall(i_delivery_service*, etch_message*, i_mailbox**);
+int tcpdelsvc_endcall (i_delivery_service*, i_mailbox*, etch_type*, etch_object**);
+int tcpdelsvc_session_message (void*, etch_who*, etch_message*);
+int tcpdelsvc_session_control (void*, etch_event*, etch_object*);
+int tcpdelsvc_session_notify (void*, etch_event*);
+etch_object* tcpdelsvc_session_query (void*, etch_query*);
+i_session* tcpdelsvc_get_session(void*);
+etch_object* tcpdelsvc_transport_query (void*, etch_query*);
+int tcpdelsvc_transport_control(void*, etch_event*, etch_object*);
+int tcpdelsvc_transport_notify (void*, etch_event*);
+
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * delivery service
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * new_etch_transport()
+ * etch_delivery_service constructor.
+ * @remarks this is the transport factory, implemented via a switch.
+ * @param uri a uri string, caller relinquishes.
+ * @param params a transport parameter bundle, caller retains.
+ * @param conximpl optional pre-existing connection implementation, such as tcp_connection*.
+ * @return a delivery service interface. note that invoking the destructor on this interface
+ * destroys the delivery service implementation, as well as the interface.
+ */
+i_delivery_service* new_etch_transport (wchar_t* uri, etch_factory_params* params, void* conximpl)
+{
+ etch_url* url = new_url(uri);
+
+ i_delivery_service* newds = new_etch_transport_a (url, params, conximpl);
+
+ etch_object_destroy(url);
+ return newds;
+}
+
+
+/*
+ * new_etch_transport_a()
+ * etch_delivery_service constructor.
+ * @remarks this is the transport factory, implemented via a switch.
+ * @param url an etch_url, caller retains.
+ * @param resources a resources map, caller retains.
+ * @return a delivery service interface. invoking the destructor on this interface
+ * destroys the delivery service implementation, as well as the interface.
+ */
+i_delivery_service* new_etch_transport_a (etch_url* url, etch_factory_params* params, void* conximpl)
+{
+ i_delivery_service* newds = NULL;
+
+ if (is_url_scheme_udp(url))
+ {
+ /* not yet implemented */
+ }
+ #if(0)
+ else /* handlers for other url schemes follow here eventually */
+ if (is_url_scheme_foo(url))
+ {
+ /* ... */
+ }
+ #endif
+ else
+ { /* url schemes http, tcp, default */
+ etch_tcp_delivery_service* tcpds = new_tcp_delivery_service (url, params, conximpl);
+
+ if (tcpds)
+ { newds = tcpds->ids;
+ newds->thisx = tcpds;
+ }
+ }
+
+ return newds;
+}
+
+
+/*
+ * new_delivery_service()
+ */
+etch_object* new_delivery_service (const int objsize, const unsigned short class_id)
+{
+ return new_object (objsize, ETCHTYPEB_DELIVERYSVC_IMPL, class_id);
+}
+
+
+/*
+ * new_tcp_delivery_service()
+ * etch_tcp_delivery_service constructor
+ * replaces java TcpTransportFactory.newTransport
+ * @param params server parameter bundle, caller retains.
+ * ¶m tcpx if present, the already accepted client connection.
+ * if present, caller retains.
+ */
+etch_tcp_delivery_service* new_tcp_delivery_service (etch_url* url,
+ etch_factory_params* params, etch_tcp_connection* tcpconx)
+{
+ etch_resources* resources = NULL;
+ etch_packetizer* packetizer = NULL;
+ etch_messagizer* messagizer = NULL;
+ etch_mailbox_manager* mboxmgr = NULL;
+ etch_tcp_delivery_service* delsvc = NULL;
+ const int is_tcpconx_owned = tcpconx == NULL;
+ ETCH_ASSERT(params && params->in_resx);
+ resources = params->in_resx;
+
+ do
+ { /* as each next higher layer of the delivery service is instantiated, it
+ * is passed passed a transport interface to the previously-instantiated
+ * layer. in each such case, note that the new layer does not own memory
+ * for the passed transport interface.
+ */
+ if (NULL == tcpconx)
+ tcpconx = new_tcp_connection (url, params->in_resx, NULL);
+
+ ETCH_ASSERT(tcpconx);
+ if (0 != init_etch_tcpconx_interfaces (tcpconx)) break;
+
+ packetizer = new_packetizer_a (tcpconx->itd, url, resources);
+ if (NULL == packetizer) break;
+
+ messagizer = new_messagizer_a (packetizer->transportpkt, url, resources);
+ if (NULL == messagizer) break;
+
+ mboxmgr = new_plain_mailbox_manager (messagizer->transportmsg,
+ url->raw, resources, params->mblock);
+ if (NULL == mboxmgr) break;
+
+ delsvc = (etch_tcp_delivery_service*) new_delivery_service
+ (sizeof(etch_tcp_delivery_service), CLASSID_TCP_DELIVERYSVC);
+
+ ((etch_object*)delsvc)->destroy = destroy_tcp_delivery_service;
+
+ /* set our transport to that of the next lower layer (mailbox manager) */
+ delsvc->transport = mboxmgr->transportmsg;
+ delsvc->transportx = mboxmgr->imanager; /* todo can we lose this ref? */
+
+ delsvc->mailboxmgr = mboxmgr;
+ delsvc->tcpconx = tcpconx;
+ delsvc->wait_up = tcpconx->cx.wait_up; /* connection up/down monitor */
+ delsvc->wait_down = tcpconx->cx.wait_down; /* connection up/down monitor */
+ delsvc->rwlock = params->mblock; /* not owned */
+ delsvc->packetizer = packetizer; /* todo can we lose these refs */
+ delsvc->messagizer = messagizer;
+ delsvc->resources = resources;
+ delsvc->is_tcpconx_owned = is_tcpconx_owned;
+
+ tcpdelsvc_init (delsvc); /* initialize the delivery service interface */
+
+ } while(0);
+
+ if (NULL == delsvc)
+ {
+ etch_object_destroy(tcpconx);
+ tcpconx = NULL;
+
+ etch_object_destroy(packetizer);
+ packetizer = NULL;
+
+ etch_object_destroy(messagizer);
+ messagizer = NULL;
+
+ etch_object_destroy(mboxmgr);
+ mboxmgr = NULL;
+
+ }
+
+ return delsvc;
+}
+
+/**
+ * tcpdelsvc_set_session()
+ * @param session the i_sessionmessage interface. caller retains ownership.
+ * this is generally called from the stub constructor.
+ */
+void tcpdelsvc_set_session (void* data, void* sessionData)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ i_sessionmessage* session = (i_sessionmessage*)sessionData;
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+ ETCH_ASSERT(is_etch_sessionmsg(session));
+ /* set delivery service session to be the passed (stub's) session */
+ tcpds->session = tcpds->ids->ism = session;
+}
+
+/**
+ * tcpdelsvc_transport_message()
+ * @param whoto recipient - caller retains this memory, can be null.
+ * @param message the message
+ * caller relinquishes this memory on success, retains on failure.
+ * @return 0 success, -1 error.
+ */
+int tcpdelsvc_transport_message (void* data, void* whoData, void* messageData)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ etch_who* whoto = (etch_who*)whoData;
+ etch_message* msg = (etch_message*)messageData;
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+ i_transportmessage* dstransport = tcpds->transport;
+ ETCH_ASSERT(is_etch_transportmsg(dstransport));
+
+ return dstransport->transport_message (dstransport->thisx, whoto, msg);
+}
+
+
+
+/**
+ * tcpdelsvc_init()
+ * initialize delivery service interface
+ */
+int tcpdelsvc_init (etch_tcp_delivery_service* delsvc)
+{
+ i_session* isession = NULL;
+ i_transport* itransport = NULL;
+
+ i_delivery_service* ids = new_delivery_service_interface(NULL, NULL);
+ delsvc->ids = ids;
+
+ /* external transport and session */
+ ids->transport = delsvc->transport;
+ ids->session = delsvc->session;
+
+ ids->begin_call = delsvc->begin_call = (etch_delivsvc_begincall)tcpdelsvc_begincall;
+ ids->end_call = delsvc->end_call = (etch_delvisvc_endcall)tcpdelsvc_endcall;
+
+
+ /* - - - - - - - - - - - - - - -
+ * i_transportmessage (native)
+ * - - - - - - - - - - - - - - -
+ */
+ itransport = new_transport_interface (ids,
+ tcpdelsvc_transport_control,
+ tcpdelsvc_transport_notify,
+ tcpdelsvc_transport_query);
+
+ delsvc->transportmsg = new_transportmsg_interface (ids,
+ tcpdelsvc_transport_message,
+ itransport); /* transportmsg now owns itransport */
+
+ delsvc->transportmsg->set_session = tcpdelsvc_set_session;
+ delsvc->transportmsg->get_session = tcpdelsvc_get_session;
+
+ /* copy native transport back to interface */
+ ids->itm = delsvc->transportmsg;
+
+ /* copy i_transportmsg interface methods up to this object */
+ delsvc->transport_message = delsvc->transportmsg->transport_message;
+ delsvc->transport_control = itransport->transport_control;
+ delsvc->transport_notify = itransport->transport_notify;
+ delsvc->transport_query = itransport->transport_query;
+ delsvc->set_session = itransport->set_session;
+ delsvc->get_session = itransport->get_session;
+
+
+ /* - - - - - - - - - - - - - - -
+ * i_sessionmessage (native)
+ * - - - - - - - - - - - - - - -
+ */
+ isession = new_session_interface (ids,
+ tcpdelsvc_session_control,
+ tcpdelsvc_session_notify,
+ tcpdelsvc_session_query);
+
+ delsvc->sessionmsg = new_sessionmsg_interface (ids,
+ tcpdelsvc_session_message,
+ isession); /* sessionmsg now owns isession */
+
+ /* copy native session back to interface */
+ ids->ism = delsvc->sessionmsg;
+
+ /* copy i_sessionmessage interface methods up to this object */
+ delsvc->session_message = delsvc->sessionmsg->session_message;
+ delsvc->session_control = isession->session_control;
+ delsvc->session_notify = isession->session_notify;
+ delsvc->session_query = isession->session_query;
+
+ /* finally set session of next lower layer (messagizer) to our session */
+ delsvc->transport->set_session (delsvc->transport->thisx, delsvc->sessionmsg);
+
+ return 0;
+}
+
+
+/**
+ * new_delivery_service_interface()
+ * delivery service interface constructor
+ */
+i_delivery_service* new_delivery_service_interface
+ (i_sessionmessage* isessionmsg, i_transportmessage* itransportmsg)
+{
+ i_delivery_service* ids = (i_delivery_service*) new_object(sizeof(i_delivery_service),
+ ETCHTYPEB_DELIVERYSVCINT, CLASSID_DELIVERYSVC);
+
+ /* this destructor destroys the parent implementation,
+ * which in turn destroys this interface object */
+ ((etch_object*)ids)->destroy = destroy_delivery_service_via_interface;
+
+ ids->ism = isessionmsg;
+ ids->itm = itransportmsg;
+
+ return ids;
+}
+
+
+/**
+ * destroy_delivery_service_stub()
+ * invoked by i_deliveryservice destructor to destroy the stub via the session
+ * shared by the stub and delivery service, and finally destroy the session.
+ */
+int destroy_delivery_service_stub (void * data)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ /* 1. the stub constructor created a session interface and set the
+ * delivery service's session to that session, and set the delivery
+ * service's is_session_owned true.
+ * 2. the stub and delivery service therefore share the session interface.
+ * 3. the delivery service implementation object (e.g. tcp delivery service)
+ * also references that session, but of course does not own it.
+ * 4. the session interface's thisx pointer is the stub object.
+ * 5. the delivery service owns the stub by contract.
+ * the delivery service implementation object's destructor invokes the
+ * delivery service interface destructor to destroy the session and stub.
+ * 6. the delivery service interface destructor destroys the stub by invoking
+ * this method, which finds the stub via the thisx of the shared session.
+ */
+ xxxx_either_stub* stubobj = NULL;
+ if (!ids->session || !ids->is_session_owned) return -1;
+
+ stubobj = ids->session->thisx; /* stub is the session interface's thisx */
+ if (is_etch_stub(stubobj))
+ ((etch_object*)stubobj)->destroy(stubobj);
+ else /* is there a use case for this */
+ { assert(is_etch_sessionmsg(ids->session));
+ etch_object_destroy(ids->session);
+ }
+
+ ids->session = NULL;
+ return 0;
+}
+
+
+/**
+ * destroy_delivery_service_interface()
+ * i_delivery_service destructor 1.
+ */
+int destroy_delivery_service_interface(void* data)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ if (NULL == ids) return -1;
+
+ if (!is_etchobj_static_content(ids))
+ {
+ /* stub owns the ism, ds owns the stub: destroy stub and session */
+ destroy_delivery_service_stub(ids);
+
+ /* stub does not own the itm: itm is that of the mailbox manager */
+ if (ids->itm && ids->is_transport_owned) {
+ etch_object_destroy(ids->itm);
+ ids->itm = NULL;
+ }
+ }
+
+ return destroy_objectex((etch_object*)ids);
+}
+
+
+/*
+ * destroy_delivery_service_via_interface()
+ * i_delivery_service destructor 2.
+ * this destructor will destroy the parent delivery service implementation, whose
+ * destructor will in turn destroy this interface object. this permits the object
+ * creating the delivery service to hold a reference to the interface only, and to
+ * destroy the implementation and interface by invoking the interface's destructor.
+ */
+int destroy_delivery_service_via_interface(void* data)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ etch_object* deliveryservice_implobj = ids? ids->thisx: NULL;
+
+ if (deliveryservice_implobj) /* etch_tcp_delivery_service, e.g. */
+ deliveryservice_implobj->destroy (deliveryservice_implobj);
+
+ return 0;
+}
+
+
+/**
+ * destroy_tcp_delivery_service()
+ * etch_tcp_delivery_service destructor
+ */
+int destroy_tcp_delivery_service (void* data)
+{
+ etch_tcp_delivery_service* thisx = (etch_tcp_delivery_service*)data;
+ const char* thistext = "delsvc dtor";
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ /* ensure any threads referencing mailboxes (see mailbox.message())
+ * have run to completion before we start tearing it down. */
+ etchmbox_get_readlockex (thisx->rwlock, thistext);
+ etchmbox_release_readlockex (thisx->rwlock, thistext);
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying packetizer ...\n");
+
+ etch_object_destroy(((etch_packetizer*)thisx->packetizer));
+ thisx->packetizer = NULL;
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying messagizer ...\n");
+
+ etch_object_destroy(((etch_messagizer*)thisx->messagizer));
+ thisx->messagizer = NULL;
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying mailbox manager ...\n");
+
+ etch_object_destroy(((etch_mailbox_manager*)thisx->mailboxmgr));
+ thisx->mailboxmgr = NULL;
+
+ /* on server side, listen thread destroys tcpconx on exit.
+ * on client side, tcpconx is destroyed here. */
+ if (thisx->is_tcpconx_owned){
+
+ etch_object_destroy(thisx->tcpconx);
+ thisx->tcpconx = NULL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying delivery interface ...\n");
+ destroy_delivery_service_interface(thisx->ids);
+
+ etch_object_destroy(thisx->sessionmsg);
+ thisx->sessionmsg = NULL;
+
+ etch_object_destroy(thisx->transportmsg);
+ thisx->transportmsg = NULL;
+
+ }
+ return destroy_objectex((etch_object*)thisx);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * i_deliveryservice (i_sessionmessage, i_transportmessage)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * get_etch_ds_impl()
+ * convenience method to verify i_delivery_service, and from it,
+ * get, verify, and return the delivery service implementation object.
+ */
+etch_tcp_delivery_service* get_etch_ds_impl (i_delivery_service* ids)
+{
+ etch_tcp_delivery_service* tcpds = NULL;
+ ETCH_ASSERT(is_etch_ideliverysvc(ids));
+ tcpds = ids->thisx;
+ ETCH_ASSERT(is_etch_deliverysvc(tcpds));
+ return tcpds;
+}
+
+
+ /**
+ * tcpdelsvc_begincall()
+ * i_deliveryservice :: begincall
+ * @param msg caller relinquishes on success, retains on failure
+ * @param out mailbox interface returned on success
+ * @return 0 success, or -1 failure. new mailbox return in out parameter.
+ */
+int tcpdelsvc_begincall (i_delivery_service* ids, etch_message* msg, i_mailbox** out)
+{
+ int result = 0;
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+ i_transportmessage* dstransport = tcpds->transport;
+ ETCH_ASSERT(is_etch_transportmsg(dstransport));
+
+ /* transport is mailbox mgr pmboxmgr_transport_call(imbmgr) */
+ result = tcpds->transportx->transport_call (tcpds->transportx, NULL, msg, out);
+
+ return result;
+}
+
+
+/**
+ * tcpdelsvc_endcall()
+ * read the response message, close its mailbox and return the result object.
+ * @param mbox the current mailbox (interface), caller retains.
+ * @param response_type type of the response message, caller retains.
+ * @param out pointer to caller's location to receive the message response object.
+ * @remarks if some exception condition occurred, the result object will not be an
+ * object of the expected result type, but rather will be an etch_mailbox_element
+ * object containing an exception. therefore, the result object should be tested
+ * with is_exception(resultobj) prior to expecting it to be of the expected type.
+ * @return 0 success, -1 failure. a result object is returned via out parameter.
+ * the result object is the expected object type of the service function result,
+ * or if a response could not be read, the etch_mailbox_element object wrapping
+ * both the reply message object, and an exception object detailing the problem.
+ * for example, if the service message is etch_int32* add(etch_int32*, etch_int32*),
+ * the result object will be an etch_int32 unless an exception occurred.
+ */
+int tcpdelsvc_endcall (i_delivery_service* ids, i_mailbox* ibox, etch_type* response_type, etch_object** out)
+{
+ int result = 0;
+ int timeout = 0;
+ etch_config_t* config = NULL;
+ int32 default_timeout = 0;
+ etch_object* result_obj = NULL;
+ etch_mailbox_element* mbe = NULL;
+ const char* thistext = "tcpdelsvc_endcall";
+ /* get the response message type's instance data */
+ etch_type_impl* typeinfo = response_type? (etch_type_impl*) response_type->impl: NULL;
+ ETCH_ASSERT(typeinfo && out);
+
+ etch_runtime_get_config(&config);
+ ETCH_ASSERT(config);
+
+ /* we do not default to wait forever in order that we can fail gracefully rather
+ * than hang the mailbox read. this behavior is essential for comprehensive testing.
+ * if a type specifies a timeout of zero, we do not interpret this as ETCH_INFWAIT,
+ * but rather we substitute a default timeout, which is configurable.
+ * if a message type actually requires an abnormally lengthy wait, it will specify
+ * some presumably large number of milliseconds as its timeout value. similarly,
+ * if we actually want to wait "forever" by default, we can configure a very large
+ * timeout value which is effectively "forever".
+ */
+ etch_config_get_property_int(config, "etch.mailbox.timeout.read", &default_timeout);
+ //TODO: no timeout value
+ timeout = typeinfo->timeout > 0 ? typeinfo->timeout : default_timeout;
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "reading mailbox (wait %d) ...\n", timeout);
+
+ /* read reply message from mailbox, waiting for message to arrive if necessary */
+ result = ibox->read_withwait(ibox, timeout, &mbe);
+
+ if(mbe == NULL) {
+ result = 0;
+ *out = (etch_object*) new_etch_exception_from_errorcode(ETCH_ERROR);
+ }
+ /* mailbox read timed out or otherwise failed */
+ else if (is_etch_exception(mbe))
+ { /* here we are returning an object of a type not expected by the service.
+ * this method's contract will specify that the app must test the returned
+ * object for an exception prior to "casting" it to the expected result
+ * object type. another possibility is that we could have default
+ * constructors for each response type.
+ */
+ *out = (etch_object*) mbe;
+ }
+ /* find the result object expected in the reply message */
+ else if (NULL == (result_obj = message_get (mbe->msg, typeinfo->response_field)))
+ {
+ etch_object_destroy(mbe);
+ *out = NULL;
+ result = 0;
+ }
+ else
+ { /* we found the reply message result object. return this result object,
+ * and destroy the message wrapper and the reply message along with it.
+ * note that we must be careful destroying the message, since the message
+ * result object, that we intend to return to the application, is part of
+ * the message and thus destroyed with the message unless steps are taken
+ * to protect it. we could clone the result and return the clone, but
+ * that would presuppose that the object is cloneable, so instead we will
+ * protect the object, destroy the message, and finally unprotect the object.
+ */
+ set_etchobj_static_all(result_obj); /* protect result object */
+ etch_object_destroy(mbe); /* destroy message and wrapper */
+ clear_etchobj_static_all(result_obj); /* unprotect result object */
+ *out = result_obj; /* return result object */
+ // ETCH_LOG(LOG_CATEGORY, ETCH_LOG_XDEBUG, "mailbox result object type %d class %d\n",
+ // ((etch_object*)result_obj)->obj_type, ((etch_object*)result_obj)->class_id);
+ result = 0;
+ }
+
+ /* acquire mailbox read write lock to switch context back to the receive thread,
+ * which will continue at the return from the queue.put in mailbox.message().
+ * we have passed in a dedicated mutex in start_xxxx_client, intended to be used
+ * by client main thread and receive thread as a read/write lock. there should
+ * be a cleaner way to sync mailbox reads and writes from within the mailbox
+ * itself, we need to investigate. ideally we would sync all of mailbox.message()
+ * using its queue waiter's mutex; however at present all such waiter mutexes are
+ * non-recursive, and so we would need to do extensive re-testing if we were to
+ * change waiter mutexes to be nestable, assuming they would work at all that way.
+ * we should make this change asap however, since the way it is now, we don't wait
+ * for the mailbox write to do its fire_notify() before we read the mailbox.
+ * however the following lock at least ensures the fire_notify() happens prior to
+ * closing the mailbox, which is the overriding consideration.
+ */
+ etchmbox_get_readlock (ibox->thisx, thistext);
+ etchmbox_release_readlock (ibox->thisx, thistext);
+
+ /* we're now done with the the mailbox so close it. */
+ ibox->close_read (ibox);
+ return result;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - -
+ * i_deliveryservice :: i_sessionmessage (i_session)
+ * - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/* this is the delivery service interface implementation of i_sessionmessage,
+ * distinct from the transport.session's implementation of i_sessionmessage
+ * which is implemented externally and set via set_session().
+ */
+
+/**
+ * tcpdelsvc_session_message()
+ * @param whofrom caller retains, can be null.
+ * @param msg caller relinquishes
+ * @return 0 (message handled), or -1 (error, closed, or timeout)
+ */
+int tcpdelsvc_session_message (void* data, etch_who* whofrom, etch_message* msg)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+ i_sessionmessage* dssession = tcpds->session;
+ ETCH_ASSERT(is_etch_sessionmsg(dssession));
+
+ return dssession->session_message(dssession->thisx, whofrom, msg);
+}
+
+
+/**
+ * tcpdelsvc_session_control()
+ * delivery service interface implementation of i_session_message
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+int tcpdelsvc_session_control (void* data, etch_event* control, etch_object* value)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+ i_sessionmessage* dssession = tcpds->session;
+ ETCH_ASSERT(is_etch_sessionmsg(dssession));
+
+ return dssession->session_control(dssession->thisx, control, value);
+}
+
+
+/**
+ * etch_tcpdelsvc_session_notify()
+ * @param evt event, caller relinquishes.
+ */
+int tcpdelsvc_session_notify (void* data, etch_event* evt)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ int result = -1, evtype = evt? evt->value: 0;
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+ i_sessionmessage* dssession = tcpds->session;
+ ETCH_ASSERT(is_etch_sessionmsg(dssession));
+
+ switch(evtype)
+ {
+ case ETCHEVT_SESSION_UP:
+ etch_wait_set(tcpds->wait_up, evtype);
+ break;
+ case ETCHEVT_SESSION_DOWN:
+ etch_wait_set(tcpds->wait_down, evtype);
+ break;
+ }
+
+ result = dssession->session_notify (dssession->thisx, evt);
+ return result;
+}
+
+/**
+ * etch_tcpdelsvc_session_query()
+ * @param query, caller relinquishes.
+ */
+etch_object* tcpdelsvc_session_query (void* data, etch_query* query)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+ i_sessionmessage* dssession = tcpds->session;
+ ETCH_ASSERT(is_etch_sessionmsg(dssession));
+
+ return dssession->session_query (dssession->thisx, query);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * i_deliveryservice :: i_transportmessage (i_transport)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+
+/**
+ * tcpdelsvc_get_session()
+ * @return a reference to the delivery service i_sessionmessage interface.
+ * caller does not own this object.
+ */
+i_session* tcpdelsvc_get_session (void* data)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+
+ return (i_session*)tcpds->session;
+}
+
+
+
+
+/**
+ * tcpdelsvc_transport_control()
+ * @param control, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ * @remarks as it currently stands, the value object passed through these transport
+ * interfaces must be a cloneable object, either through being cloneable by default,
+ * such as the etch_primitive or etch_object derivatives (for example, etch_int32,
+ * etch_string, etch_date, etch_event, etch_object, and others); or by virtue of
+ * having custom clone() functions assigned to them.
+ */
+int tcpdelsvc_transport_control (void* data, etch_event* control, etch_object* valobj)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ int result = 0;
+ etch_connection* cx = NULL;
+ i_transportmessage* dstransport = NULL;
+ etch_tcp_delivery_service* tcpds = NULL;
+ const int objclass = control? ((etch_object*)control)->class_id: 0;
+ const int timeoutms = control && (is_etch_int32(valobj))? ((etch_int32*)control)->value: 0;
+ ETCH_ASSERT(is_etch_ideliverysvc(ids) && objclass);
+
+ tcpds = get_etch_ds_impl(ids); /* delivery service implementation */
+ ETCH_ASSERT(is_etch_deliverysvc(tcpds));
+ dstransport = tcpds->transport; /* delivery service transport (mailbox mgr) */
+ ETCH_ASSERT(is_etch_transportmsg(dstransport));
+ cx = &tcpds->tcpconx->cx; /* underlying connection */
+
+ switch(objclass) /* forward the transport event */
+ {
+ case CLASSID_CONTROL_START_WAITUP:
+ /* point to the condition variable on the waiter. the need to do this
+ * is a semikludge; that is, having to set some state prior to calling
+ * waitup; however we can't have the function call preset the state
+ * because the absence of the state variable means "not waiting", and
+ * is tested for by the wait function. so we need to have a target for
+ * the "up" state before we do the waitup, since the connect will complete
+ * before we get around to asking for the waitup, and it needs to be able
+ * to mark state as up, thus we set that target below. previously the state
+ * variable cond_var was not set until the wait_up was invoked. in the
+ * current design the cond_var is nulled out after a wait, in order to
+ * reset wait state to not waiting, so we need to ensure it is populated
+ * in advance of any need to set a wait condition to some state, prior to
+ * actually waiting.
+ */
+ //etchconx_init_waitstate (cx); /* see comment above */
+ ((etch_object*)control)->class_id = CLASSID_CONTROL_START; /* modify event to not wait */
+
+ result = dstransport->transport_control (dstransport->thisx, control, valobj);
+
+ if (0 == result)
+ result = etchconx_wait_up (cx, timeoutms);
+
+ break;
+
+ case CLASSID_CONTROL_STOP_WAITDOWN:
+ //etchconx_init_waitstate (cx); /* see comment above */
+ ((etch_object*)control)->class_id = CLASSID_CONTROL_STOP; /* modify event to not wait */
+
+ result = dstransport->transport_control (dstransport->thisx, control, valobj);
+
+ if (0 == result)
+ result = etchconx_wait_down (cx, timeoutms);
+
+ break;
+
+ case CLASSID_CONTROL_START:
+ case CLASSID_CONTROL_STOP:
+ default: /* event not of interest here so pass it on */
+
+ result = dstransport->transport_control (dstransport->thisx, control, valobj);
+ }
+
+ return result;
+}
+
+
+/**
+ * tcpdelsvc_transport_notify()
+ * @param evt, caller relinquishes.
+ */
+int tcpdelsvc_transport_notify (void* data, etch_event* evt)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+
+ return tcpds->transport->transport_notify( tcpds->transport->thisx, evt);
+}
+
+
+/**
+ * tcpdelsvc_transport_query()
+ * i_transportmessage::transport_query override.
+ * @param query, caller relinquishes.
+ */
+etch_object* tcpdelsvc_transport_query (void* data, etch_query* query)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ int result = 0;
+ etch_object* resultobj = NULL;
+ etch_connection* cx = NULL;
+ const int timeoutms = query? query->value: 0;
+ const int objclass = query? ((etch_object*)query)->class_id: 0;
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+ i_transportmessage* dstransport = tcpds->transport;
+ cx = &tcpds->tcpconx->cx;
+
+ switch(objclass)
+ {
+ case CLASSID_QUERY_WAITUP:
+ result = etchconx_wait_up(cx, timeoutms);
+ break;
+
+ case CLASSID_QUERY_WAITDOWN:
+ result = etchconx_wait_down(cx, timeoutms);
+ break;
+
+ default:
+ resultobj = dstransport->transport_query (dstransport->thisx, query);
+ query = NULL; /* argument was relinquished */
+ }
+
+ if (query)
+ etch_object_destroy(query);
+ return resultobj;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * etch_resources
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * etch_transport_resources_init()
+ * @return transport resources map initialized with default items and values
+ */
+etch_resources* etch_transport_resources_init(etch_resources* resxmap)
+{
+ const int SESSIONPOOL_INITSIZE = 4;
+
+ etch_resources* resources = resxmap? resxmap: new_etch_resources(0);
+
+ /* these threadpools are session thread managers, i.e. they manage
+ * a pool of stub listener threads on either side. */
+ if (NULL == etch_resources_get (resources, ETCH_RESXKEY_POOLTYPE_QUEUED))
+ { /* until we implement a queued pool, we use a free pool */
+ etch_threadpool* threadpool
+ = new_threadpool (ETCH_THREADPOOLTYPE_FREE, SESSIONPOOL_INITSIZE);
+
+ etch_resources_add (resources, (wchar_t*)ETCH_RESXKEY_POOLTYPE_QUEUED,
+ (etch_object*) threadpool);
+ }
+
+ if (NULL == etch_resources_get (resources, ETCH_RESXKEY_POOLTYPE_FREE))
+ {
+ etch_threadpool* threadpool
+ = new_threadpool (ETCH_THREADPOOLTYPE_FREE, SESSIONPOOL_INITSIZE);
+
+ etch_resources_add (resources, (wchar_t*)ETCH_RESXKEY_POOLTYPE_FREE,
+ (etch_object*)threadpool);
+ }
+
+ if (NULL == etch_resources_get (resources, ETCH_RESXKEY_MSGIZER_FORMAT))
+ {
+ etch_resources_add (resources, (wchar_t*)ETCH_RESXKEY_MSGIZER_FORMAT,
+ (etch_object*)new_stringw(ETCH_RESXVAL_XPORTFMT_BINARY));
+ }
+
+ return resources;
+}
+
+
+/*
+ * get_etch_transport_resources()
+ * @return transport resources map initialized with default items and values
+ */
+etch_resources* get_etch_transport_resources(etch_resources* resxmap)
+{
+ etch_resources* outresx = NULL;
+ ETCH_ASSERT(resxmap == NULL || is_etch_hashtable(resxmap));
+
+ outresx = etch_transport_resources_init(resxmap);
+
+ ETCH_ASSERT(outresx);
+ return outresx;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * server/client "factories"
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/*
+ * destroy_etch_clientsession()
+ * destructor for a server's client session's instance data.
+ */
+int destroy_etch_clientsession (void* data)
+{
+ etch_session* thisx = (etch_session*)data;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+
+ etch_object_destroy(thisx->ds);
+ etch_object_destroy(thisx->server_stub);
+ etch_object_destroy(thisx->server);
+ etch_object_destroy(thisx->client);
+ /* note that we do not destroy the conximpl (accepted tcp connection)
+ * here, the receive thread destroys it instead */
+
+ /* remove this entry from active sessions list.
+ * note that thisx.thisx is the serverparams hosting the sessions list */
+ remove_etch_session (thisx->thisx, thisx->session_id);
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
+
+
+/*
+ * new_etch_clientsession()
+ * constructor for session parameter bundle.
+ * this object wraps all of a server's per-session instance data.
+ */
+etch_session* new_etch_clientsession (void* host, etch_connection* cx)
+{
+ etch_session* newobj = (etch_session*) new_object(sizeof(etch_session),
+ ETCHTYPEB_CLIENT_SESSION, CLASSID_CLIENT_SESSION);
+
+ ((etch_object*)newobj)->destroy = destroy_etch_clientsession;
+ newobj->thisx = host; /* etch_serverparams* */
+ newobj->cx = cx;
+ newobj->session_id = cx->conxid; /* session key */
+
+ /* carrying the session instance data with the connection is not good design,
+ * however we need it in the receive loop listener threadproc which does not
+ * see transport.h. when we figure another way to get the session's data
+ * through to the listener thread, we should remove it from the connection,
+ * since session data is at a much higher level than connection of course.
+ */
+ cx->session = (etch_object*) newobj;
+
+ return newobj;
+}
+
+
+/*
+ * get_etch_session()
+ * look up session instance data object matching specified session ID.
+ * @return index of entry, or -1 if not found.
+ * array entry is returned via out parameter if specified.
+ */
+int get_etch_session (etch_server_factory* sf, const int session_id, etch_session** out)
+{
+ int ndx = 0, retndx = -1;
+ etch_iterator iterator;
+ etch_session *s = NULL;
+ ETCH_ASSERT(is_etch_factoryparams(sf));
+
+ etch_arraylist_getlock(sf->clientlist);
+
+ set_iterator(&iterator, sf->clientlist, &sf->clientlist->iterable);
+ while(iterator.has_next(&iterator))
+ { if ((NULL != (s = iterator.current_value)) && (s->session_id == session_id))
+ { if (out) *out = s;
+ retndx = ndx;
+ break;
+ }
+ ndx++;
+ iterator.next(&iterator);
+ }
+
+ etch_arraylist_rellock(sf->clientlist);
+
+ return retndx;
+}
+
+
+/*
+ * remove_etch_session()
+ * remove session instance data object matching specified session ID.
+ * does not destroy list item content but rather returns it for caller disposition.
+ * @return the removed object, or NULL if not found.
+ */
+etch_session* remove_etch_session (etch_server_factory* sf, const int session_id)
+{
+ etch_session* foundsession = NULL;
+ int whichndx = 0;
+
+ etch_arraylist_getlock(sf->clientlist);
+ whichndx = get_etch_session (sf, session_id, &foundsession);
+
+ if (whichndx >= 0) {
+ etch_arraylist_remove (sf->clientlist, whichndx, FALSE);
+ }
+ etch_arraylist_rellock(sf->clientlist);
+
+ return foundsession;
+}
+
+
+/*
+ * destroy_etch_client_factory()
+ * client params destructor.
+ * destroys the ancillary objects attached to a remote server.
+ */
+int destroy_etch_client_factory (void* data)
+{
+ etch_client_factory* thisx = (etch_client_factory*)data;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ if (thisx->iclient) /* destroy i_xxxx_client */
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying client interface ...\n");
+
+ if(((etch_object*)thisx->iclient))
+ ((etch_object*)thisx->iclient)->destroy(((etch_object*)thisx->iclient));
+ thisx->iclient = NULL;
+ }
+
+ if (thisx->stub) /* destroy xxxx_client_stub */
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying stub ...\n");
+ if(((etch_object*)thisx->stub))
+ ((etch_object*)thisx->stub)->destroy(((etch_object*)thisx->stub));
+ thisx->stub = NULL;
+ }
+
+ /* destroy i_delivery_service */
+ if (thisx->dsvc)
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying delivery service ...\n");
+ if(thisx->dsvc)
+ etch_object_destroy(thisx->dsvc);
+ thisx->dsvc = NULL;
+
+ }
+
+ /* destroy etch_resources */
+ if (thisx->in_resx)
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying resources ...\n");
+ etch_object_destroy(thisx->in_resx);
+ thisx->in_resx = NULL;
+ }
+
+ etch_object_destroy(thisx->mainpool);
+ thisx->mainpool = NULL;
+
+ etch_mutex_destroy(thisx->rwlock);
+ thisx->rwlock = NULL;
+
+ }
+
+ return destroy_objectex(((etch_object*)thisx));
+}
+
+
+/*
+ * new_client_factory()
+ * constructor for client parameter bundle.
+ */
+etch_client_factory* new_client_factory (etch_object* session, i_session* isession, main_client_create_func main_client_create)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ const int MAINPOOL_INITIAL_SIZE = 4;
+
+ etch_client_factory* cf = (etch_client_factory*) new_object
+ (sizeof(etch_client_factory), ETCHTYPEB_FACTORYPARAMS, CLASSID_CLIENTFACTORY);
+
+ ((etch_object*)cf)->destroy = destroy_etch_client_factory;
+
+ cf->new_client = main_client_create;
+
+ cf->mainpool = new_threadpool (ETCH_THREADPOOLTYPE_FREE, MAINPOOL_INITIAL_SIZE);
+
+ // TODO: pool
+ status = etch_mutex_create(&cf->rwlock, ETCH_MUTEX_UNNESTED, NULL);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+
+ return cf;
+}
+
+
+/*
+ * destroy_etch_server_factory()
+ * server params destructor.
+ */
+int destroy_etch_server_factory (void* data)
+{
+ etch_server_factory* thisx = (etch_server_factory*)data;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ etch_object_destroy(thisx->clientlist);
+ thisx->clientlist = NULL;
+
+ etch_object_destroy(thisx->mainpool);
+ thisx->mainpool = NULL;
+
+ etch_object_destroy(thisx->subpool);
+ thisx->subpool = NULL;
+
+ etch_mutex_destroy(thisx->mblock);
+ thisx->mblock = NULL;
+
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
+
+
+/*
+ * new_server_factory()
+ * constructor for server parameter bundle.
+ * fyi invoked from this.new_etch_listener()
+ */
+etch_server_factory* new_server_factory (etch_object* session, i_session* isession,
+ helper_listener_create_func helper_listener_create, main_server_create_func main_server_create)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ const int MAINPOOL_INITIAL_SIZE = 4, SUBPOOL_INITIAL_SIZE = 4;
+
+ etch_server_factory* sf = (etch_server_factory*) new_object(sizeof(etch_server_factory), ETCHTYPEB_FACTORYPARAMS, CLASSID_SERVERFACTORY);
+
+ ((etch_object*)sf)->destroy = destroy_etch_server_factory;
+
+ sf->clientlist = new_etch_arraylist_synchronized(ETCH_DEFSIZE, ETCH_DEFSIZE);
+ sf->clientlist->content_type = ETCHARRAYLIST_CONTENT_SIMPLE;
+ sf->clientlist->is_readonly = TRUE; /* array does not own its content */
+
+ sf->helper_new_listener = helper_listener_create;
+ sf->main_new_server = main_server_create;
+ sf->session = session;
+ sf->isession = isession;
+
+ sf->mainpool = new_threadpool (ETCH_THREADPOOLTYPE_FREE, MAINPOOL_INITIAL_SIZE);
+ sf->subpool = new_threadpool (ETCH_THREADPOOLTYPE_FREE, SUBPOOL_INITIAL_SIZE);
+
+ // TODO: pool
+ status = etch_mutex_create(&sf->mblock, ETCH_MUTEX_UNNESTED, NULL);
+ ETCH_ASSERT(status == ETCH_SUCCESS);
+
+ return sf;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * transport listener
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+
+/*
+ * tcpxfact_get_session()
+ * return session interface from the server factory bundle.
+ * validate and assert the i_sessionlistener object.
+ */
+i_session* tcpxfact_get_session (i_sessionlistener* lxr)
+{
+ i_session* session = NULL;
+ etch_server_factory* factory = NULL;
+ ETCH_ASSERT(is_etch_sessionlxr(lxr));
+ factory = lxr->server_params;
+ session = factory? factory->isession: NULL;
+ return session;
+}
+
+
+/*
+ * tcpxfact_session_control()
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+int tcpxfact_session_control (void* data, etch_event* control, etch_object* value)
+{
+ i_sessionlistener* thisx = (i_sessionlistener*)data;
+ int result = -1;
+ i_session* session = tcpxfact_get_session (thisx);
+
+ if (session && session->session_control)
+ result = session->session_control (session, control, value);
+ else
+ {
+ etch_object_destroy(control);
+ etch_object_destroy(value);
+ }
+
+ return result;
+}
+
+
+/*
+ * tcpxfact_session_notify()
+ * @param evt event, caller relinquishes.
+ */
+int tcpxfact_session_notify (void* data, etch_event* evt)
+{
+ i_sessionlistener* thisx = (i_sessionlistener*)data;
+ int result = -1;
+ i_session* session = tcpxfact_get_session (thisx);
+
+ if (session && session->session_notify)
+ result = session->session_notify (session, evt);
+ else
+ etch_object_destroy(evt);
+
+ return result;
+}
+
+
+/*
+ * tcpxfact_session_query()
+ * @param query caller relinquishes
+ */
+etch_object* tcpxfact_session_query (void* data, etch_query* query)
+{
+ i_sessionlistener* thisx = (i_sessionlistener*)data;
+ void* resultobj = NULL;
+ i_session* session = tcpxfact_get_session (thisx);
+
+ if (session && session->session_query)
+ resultobj = session->session_query (session, query);
+ else
+ etch_object_destroy(query);
+
+ return resultobj;
+}
+
+
+/*
+ * transport_thread_id()
+ * return thread_id for thread zero on the main pool of this listener.
+ */
+int transport_thread_id (i_sessionlistener* listener)
+{
+ etch_server_factory* sf = NULL; etch_thread* thread0 = NULL;
+ ETCH_ASSERT(listener && is_etch_serverparams(listener->server_params));
+ sf = (etch_server_factory*) listener->server_params;
+ ETCH_ASSERT(sf->mainpool);
+ thread0 = threadpool_thread (sf->mainpool, 0);
+ return thread0? thread0->params.etch_thread_id: 0;
+}
+
+
+/*
+ * transport_session_count()
+ * return count of outstanding client sessions for this server.
+ */
+int transport_session_count (i_sessionlistener* listener)
+{
+ etch_server_factory* sf = NULL;
+ if (NULL == listener || NULL == listener->server_params) return 0;
+ sf = listener->server_params;
+ if (NULL == sf || NULL == sf->clientlist) return 0;
+ return sf->clientlist->count;
+}
+
+
+/*
+ * tcpxfact_teardown_client_sessions()
+ * signal and wait for each session thread to exit, destroying each
+ * thread, connection and session. tearing down the session destroys its
+ * delivery service, remote client, and stub. this is intended to be invoked
+ * only at server shutdown, after the main (accept) thread has exited.
+ */
+int transport_teardown_client_sessions (i_sessionlistener* listener)
+{
+ etch_iterator iterator;
+ etch_session* session = NULL;
+ etch_server_factory* sf = listener->server_params;
+ set_iterator (&iterator, sf->clientlist, &sf->clientlist->iterable);
+
+ while(iterator.has_next(&iterator)) /* for each extant client session ... */
+ {
+
+ if (NULL != (session = iterator.current_value))
+ {
+ if (is_etch_connection(session->cx))
+ {
+ session->cx->is_started = FALSE; /* mark connection stopped */
+
+ if (is_etch_thread(session->cx->thread)){
+ etch_join (session->cx->thread); /* BLOCK for thread exit */
+ }
+ }
+ //etch_object_destroy(session->conximpl);
+ //session->conximpl = NULL;
+ //etch_object_destroy(session); /* teardown this session */
+ }
+
+ iterator.next(&iterator);
+ }
+
+ return 0;
+}
+
+
+/*
+ * etch_listener_waitfor_exit()
+ * block until accept listener thread exits.
+ */
+int etch_listener_waitfor_exit (i_sessionlistener* thisx)
+{
+ etch_server_factory* p = thisx->server_params;
+ const int result = threadpool_waitfor_all (p->mainpool, FALSE);
+ return result;
+}
+
+
+/*
+ * tcpxfact_session_accepted()
+ * override for transport factory session_accepted()
+ * signature is typedef int (*etch_session_accepted) (void* thisx, void* socket);
+ * parallels java TcpTransportFactory.newListener.newSessionListener.sessionAccepted
+ * @param thisx the i_sessionlistener quasi interface
+ * @param socket an open accept raw socket, wrapped by etch_socket. caller relinquishes.
+ * in practice this is an apr socket wrapped by etch_socket.
+ * @return 0 success, -1 failure.
+ */
+int tcpxfact_session_accepted (void* data, void* connectionData)
+{
+ i_sessionlistener* thisx = (i_sessionlistener*)data;
+ etch_tcp_connection* tcpconx = (etch_tcp_connection*)connectionData;
+ int result = 0;
+ void* newstub = NULL;
+ etch_session* newsession = NULL;
+ etch_server_factory* params = NULL;
+ etch_connection* cx = &tcpconx->cx;
+ i_delivery_service* delivery_service = NULL;
+ const int session_id = cx->conxid;
+ ETCH_ASSERT(is_etch_sessionlxr(thisx));
+ ETCH_ASSERT(is_etch_tcpconnection(tcpconx));
+ params = (etch_server_factory*) thisx->server_params;
+ ETCH_ASSERT(params && params->helper_new_listener);
+
+ /* fyi java binding makes a copy of the generic resources here.
+ * we instead will not use resources for client-specific map entries,
+ * but rather will use the parameter bundle. TODO allocate client-specific
+ * segment of parameter bundle.
+ */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "creating client session %d ...\n", session_id);
+
+ /* instantiate delivery service */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "creating delivery service ...\n");
+
+ delivery_service = new_etch_transport_a (thisx->url, thisx->server_params, tcpconx);
+
+ if (NULL == delivery_service)
+ { ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "could not create delivery service\n");
+ return -1;
+ }
+
+ ETCH_ASSERT(delivery_service->itm && delivery_service->itm->transport_control);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "delivery service created\n");
+
+ newsession = new_etch_clientsession (params, cx);
+ newsession->mainlistener = thisx; /* session points back to accept listener */
+ newsession->ds = delivery_service;
+ newsession->conximpl = (etch_object*) tcpconx;
+
+ /* CALL BACK to helper.xxx_helper_listener_create to create this
+ * client's server side listener, server implementation, and stub.
+ */
+ newstub = params->helper_new_listener(params, newsession);
+ ETCH_ASSERT(is_etch_stub(newstub));
+
+ etch_arraylist_add (params->clientlist, newsession);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "client session %d created\n", session_id);
+
+ /* START this client's individual listener. since we have an accepted socket
+ * in hand, it is in effect already started.
+ */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "starting client session %d ...\n", session_id);
+ result = delivery_service->itm->transport_control (delivery_service,
+ new_etch_event(CLASSID_CONTROL_START, 0), NULL);
+
+ return result;
+}
+
+/*
+ * destroy_etch_listener()
+ * etch_listener destructor.
+ */
+int destroy_etch_listener (void* data)
+{
+ i_sessionlistener* thisx = (i_sessionlistener*)data;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx)) {
+ etch_object_destroy(thisx->url);
+ thisx->url = NULL;
+
+ if (thisx->is_session_owned)
+ etch_free(thisx->isession);
+
+ if (thisx->is_transport_owned)
+ etch_free(thisx->itransport);
+
+ if (thisx->is_resources_owned)
+ {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying resources ...\n");
+ etch_object_destroy(thisx->resources);
+ thisx->resources = NULL;
+ }
+
+ if (thisx->thisx)
+ { /* watch this spot: the i_sessionlistener and the etch_tcp_server
+ * have mutual references. we must ensure that if we are to
+ * destroy the etch_tcp_server via the i_sessionlistener, that the
+ * etch_tcp_server does not also destroy the i_sessionlistener. */
+ etch_tcp_server* srvobj = (etch_tcp_server*) thisx->thisx;
+ ETCH_ASSERT(is_etch_tcpserver(srvobj));
+ etch_object_destroy(srvobj);
+ }
+
+ if (thisx->server_params)
+ {
+ etch_server_factory* sf = thisx->server_params;
+ ETCH_ASSERT(is_etch_serverparams(sf));
+ etch_object_destroy(sf);
+ }
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
+
+
+/*
+ * new_etch_listener()
+ * constructs a new transport listener used to construct server sessions.
+ * @param uri a uri string, caller relinquishes.
+ * @param resx a resources map, caller relinquishes. currently ALWAYS NULL.
+ * @param helper_new_server_funcptr pointer to the listener ctor in server helper.
+ * @param main_new_server_funcptr pointer to the server impl ctor in main.
+ * @param get_xxxx_resources_funcptr helper new service resources callback.
+ * @return an i_sessionlistener interface. caller owns it. note that java binding
+ * returns a transport interface, whereas c binding will instead extract the
+ * transport interface from i_sessionlistener.itransport.
+ */
+i_sessionlistener* new_etch_listener (wchar_t* uri, etch_resources* resx,
+ void* factory_thisx,
+ helper_listener_create_func helper_listener_create,
+ main_server_create_func main_server_create,
+ helper_resources_init_func helper_resources_init)
+{
+ etch_tcp_server* tcp_server = NULL;
+ etch_server_factory* params = NULL;
+ etch_url* url = new_url(uri);
+
+ /* listener assumes the session interface of the server factory creator.
+ * this accomplishes the same thing as the session method implementations
+ * found in java TcpTransportFactory.newListener().
+ */
+ i_session* isession = new_session_interface (NULL,
+ tcpxfact_session_control,
+ tcpxfact_session_notify,
+ tcpxfact_session_query);
+
+ /* create the listener interface, specifying the on_session_accepted
+ * callback to be invoked on each successful server accept in order
+ * to create a new server. relinquish isession to listener here. */
+ i_sessionlistener* listener = new_sessionlistener_interface (NULL,
+ tcpxfact_session_accepted, isession);
+
+ ((etch_object*)listener)->destroy = destroy_etch_listener;
+ listener->wait_exit = etch_listener_waitfor_exit;
+ listener->url = url; /* relinquished */
+
+ /* create server "factory", which is in the c binding a parameter
+ * bundle which includes callbacks to the new server constructors */
+ params = new_server_factory ((etch_object*) listener, listener->isession, helper_listener_create, main_server_create);
+ params->thisx = factory_thisx;
+
+ /* instantiate generic resources and call back to specific helper for vf */
+ listener->is_resources_owned = TRUE;
+ listener->resources = get_etch_transport_resources (resx); /* resx null */
+ params->in_resx = listener->resources;
+ params->in_uri = uri;
+ helper_resources_init(params);
+ listener->server_params = params;
+ /* fyi params delivery service is set later, in svr->on_session_accepted(),
+ whose implementation is tcpxfact_session_accepted(), in this module */
+
+ /* create the tcp connection and acceptor SVR BREAK 001 */
+ tcp_server = new_tcp_server (url, params->mainpool, params->subpool, resx, listener);
+
+ if (NULL == tcp_server) {
+ etch_object_destroy(listener);
+ return NULL;
+ }
+
+ /* listener [main] expects that i_sessionlistener.thisx is the server,
+ * e.g. an etch_tcp_server* */
+ listener->thisx = tcp_server;
+
+ /* copy server object's session virtuals to this object */
+ /* see java TcpTransportFactory.newListener() for session impls */
+ listener->session = tcp_server->session;
+ listener->isession = tcp_server->isession;
+ listener->session_control = tcp_server->session_control;
+ listener->session_notify = tcp_server->session_notify;
+ listener->session_query = tcp_server->session_query;
+
+ /* set this listener object's transport to be the server connection's transport */
+ ETCH_ASSERT(tcp_server->itransport);
+ etch_free(listener->itransport); /* TODO don't instantiate in the first place */
+ listener->itransport = tcp_server->itransport;
+ listener->transport_control = tcp_server->transport_control;
+ listener->transport_notify = tcp_server->transport_notify;
+ listener->transport_query = tcp_server->transport_query;
+ listener->set_session = tcp_server->set_session;
+ listener->get_session = tcp_server->get_session;
+ listener->is_transport_owned = FALSE;
+
+ return listener; /* caller owns this object */
+}
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_transport_data.c b/binding-c/runtime/c/src/main/transport/etch_transport_data.c
new file mode 100644
index 0000000..94c3a7f
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_transport_data.c
@@ -0,0 +1,86 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_transportdata.c
+ * i_transportdata interface
+ */
+
+#include "etch_transport_data.h"
+#include "etch_message.h"
+#include "etch_log.h"
+#include "etch_flexbuffer.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+
+/*
+ * destroy_transportdata()
+ * i_transportdata destructor
+ */
+int destroy_transportdata(void* data)
+{
+ i_transportdata* sm = (i_transportdata*)data;
+ if (NULL == sm) return -1;
+
+ if (!is_etchobj_static_content(sm))
+ { etch_free(sm->itransport);
+ }
+
+ return destroy_objectex((etch_object*)sm);
+}
+
+/*
+ * etchtransportdata_def_transportdata()
+ * default virtual implementation
+ * @param whoto caller retains
+ * @param buf caller retains
+ */
+int etchtransportdata_def_transportdata (void* data, void* who, void* buffer)
+{
+ return -1;
+}
+
+/**
+ * new_transportdata_interface()
+ * i_transportdata constructor
+ * @param func i_transportdata::transport_data function override.
+ * @param itransport transport interface virtual function overrides,
+ * caller relinquishes ownership of this memory
+ */
+i_transportdata* new_transportdata_interface(void* thisx, etch_transport_data func, i_transport* itransport)
+{
+ i_transportdata* newi = (i_transportdata*) new_object
+ (sizeof(i_transportdata), ETCHTYPEB_TRANSPORTDATA, CLASSID_TRANSPORTDATA);
+
+ ((etch_object*)newi)->clone = clone_null;
+ ((etch_object*)newi)->destroy = destroy_transportdata;
+
+ newi->transport_data = func? func: etchtransportdata_def_transportdata;
+
+ newi->itransport = itransport? itransport: new_default_transport_interface();
+ newi->transport_control = newi->itransport->transport_control;
+ newi->transport_notify = newi->itransport->transport_notify;
+ newi->transport_query = newi->itransport->transport_query;
+ newi->get_session = newi->itransport->get_session;
+ newi->set_session = newi->itransport->set_session;
+
+ newi->thisx = thisx;
+
+ return newi;
+}
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_transport_message.c b/binding-c/runtime/c/src/main/transport/etch_transport_message.c
new file mode 100644
index 0000000..1640f87
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_transport_message.c
@@ -0,0 +1,92 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_transportmsg.c
+ * i_transportmessage interface
+ */
+
+#include "etch_transport_message.h"
+#include "etch_message.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+
+
+/*
+ * etch_msghandler_defmessage()
+ * default virtual implementation
+ * @param sender cller retains
+ * @param msg
+ */
+int etchtransportmsg_def_transportmessage (void* data, void* sender, void* msg)
+{
+ return -1;
+}
+
+/*
+ * destroy_transportmessage()
+ * i_transportmessage destructor
+ */
+int destroy_transportmessage(void* data)
+{
+ i_transportmessage* sm = (i_transportmessage*)data;
+ if (NULL == sm) return -1;
+
+ if (!is_etchobj_static_content(sm))
+ { etch_free(sm->itransport);
+ }
+
+ return destroy_objectex((etch_object*)sm);
+}
+
+/**
+ * new_transportmsg_interface()
+ * i_transportmessage constructor
+ * @param itransport transport interface virtual function overrides,
+ * caller relinquishes ownership of this memory
+ * @param itransport transport interface virtual function overrides,
+ * caller relinquishes ownership of this memory
+ */
+i_transportmessage* new_transportmsg_interface(void* thisx,
+ etch_transport_message tm, i_transport* itransport)
+{
+ i_transportmessage* newi = (i_transportmessage*) new_object
+ (sizeof(i_transportmessage), ETCHTYPEB_TRANSPORTMSG, CLASSID_TRANSPORTMSG);
+
+ ((etch_object*)newi)->clone = clone_null;
+ ((etch_object*)newi)->destroy = destroy_transportmessage;
+
+ newi->transport_message = tm? tm: etchtransportmsg_def_transportmessage;
+
+ newi->itransport = itransport? itransport: new_default_transport_interface();
+ newi->transport_control = newi->itransport->transport_control;
+ newi->transport_notify = newi->itransport->transport_notify;
+ newi->transport_query = newi->itransport->transport_query;
+ newi->get_session = newi->itransport->get_session;
+ newi->set_session = newi->itransport->set_session;
+
+ newi->thisx = thisx;
+
+ return newi;
+}
+
+
+
+
diff --git a/binding-c/runtime/c/src/main/transport/etch_transport_packet.c b/binding-c/runtime/c/src/main/transport/etch_transport_packet.c
new file mode 100644
index 0000000..67bce3f
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_transport_packet.c
@@ -0,0 +1,99 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_transportpkt.c
+ * i_transportpacket interface
+ */
+
+#include "etch_transport_packet.h"
+#include "etch_message.h"
+#include "etch_flexbuffer.h"
+#include "etch_objecttypes.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+/*
+static const char* LOG_CATEGORY = "etch_transportpkg";
+*/
+#define ETCHPACKET_DEFHEADERSIZE 8
+
+int etchtransportpkt_def_transportpacket (void* tp, void* sender, void* buffer)
+{
+ return -1;
+}
+
+/*
+ * etchtransportpkt_def_headersize()
+ * default implementation of get_header_size()
+ * @return size of packet header in bytes
+ */
+int etchtransportpkt_def_headersize (void* data)
+{
+ i_transportpacket* tp = (i_transportpacket*)data;
+ return tp->header_size;
+}
+
+
+/*
+ * destroy_transportpacket()
+ * i_transportpacket destructor
+ */
+int destroy_transportpacket(void* data)
+{
+ i_transportpacket* itp = (i_transportpacket*)data;
+ ETCH_ASSERT(is_etch_transportpkt(itp));
+
+ if (!is_etchobj_static_content(itp))
+ { etch_free(itp->itransport);
+ }
+
+ return destroy_objectex((etch_object*) itp);
+}
+
+
+/**
+ * new_transportpkt_interface()
+ * i_transportpacket constructor
+ * @param tp transport_packet virtual function overrides,
+ * @param itransport transport interface virtual function overrides,
+ * caller relinquishes ownership of this memory
+ */
+i_transportpacket* new_transportpkt_interface(void* thisx, etch_transport_packet tp, i_transport* itransport)
+{
+ i_transportpacket* newi = (i_transportpacket*) new_object
+ (sizeof(i_transportpacket), ETCHTYPEB_TRANSPORTPKT, CLASSID_TRANSPORTPKT);
+
+ ((etch_object*)newi)->clone = clone_null;
+ ((etch_object*)newi)->destroy = destroy_transportpacket;
+ newi->thisx = thisx;
+ newi->header_size = ETCHPACKET_DEFHEADERSIZE;
+
+ newi->transport_packet = tp? tp: etchtransportpkt_def_transportpacket;
+
+ newi->itransport = itransport? itransport: new_default_transport_interface(thisx);
+
+ newi->transport_control = newi->itransport->transport_control;
+ newi->transport_notify = newi->itransport->transport_notify;
+ newi->transport_query = newi->itransport->transport_query;
+ newi->get_session = newi->itransport->get_session;
+ newi->set_session = newi->itransport->set_session;
+
+ newi->get_headersize = etchtransportpkt_def_headersize;
+
+ return newi;
+}
diff --git a/binding-c/runtime/c/src/main/transport/etch_url.c b/binding-c/runtime/c/src/main/transport/etch_url.c
new file mode 100644
index 0000000..acd8f75
--- /dev/null
+++ b/binding-c/runtime/c/src/main/transport/etch_url.c
@@ -0,0 +1,649 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * etch_url.c -- URL class, ported from etch java binding
+ * only methods needed for the c binding were ported.
+ */
+#include "etch_url.h"
+#include "etch_exception.h"
+#include "etch_map.h"
+#include "etch_log.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_mem.h"
+#include <wchar.h>
+
+int etchurl_parse (etch_url*, wchar_t*);
+int etchurl_parseterms (etch_url*, wchar_t*);
+int etchurl_parseparams(etch_url*, wchar_t*);
+void etchurl_parsehost (etch_url*, wchar_t*);
+wchar_t* etchurl_unescape (etch_url*, wchar_t*);
+int etchurl_is_emptystring(wchar_t*);
+etch_arraylist* new_etchurl_paramlist();
+etch_hashtable* new_etchurl_termmap();
+etch_set* new_etch_term_consolidator_set();
+
+
+/**
+ * destroy_url()
+ * etch_url destructor.
+ */
+int destroy_url(void* data)
+{
+ etch_url* url = (etch_url*)data;
+ if (!is_etchobj_static_content(url))
+ { if (url->raw) etch_free(url->raw);
+ if (url->scheme) etch_free(url->scheme);
+ if (url->user) etch_free(url->user);
+ if (url->password) etch_free(url->password);
+ if (url->fragment) etch_free(url->fragment);
+ if (url->host) etch_free(url->host);
+ if (url->uri) etch_free(url->uri);
+
+ etch_object_destroy(url->params);
+ etch_object_destroy(url->terms);
+ }
+
+ return destroy_objectex((etch_object*)url);
+}
+
+
+/**
+ * new_url()
+ * etch_url constructor.
+ * @param urlstring the raw url string. caller retains ownership.
+ */
+etch_url* new_url(wchar_t* urlstring)
+{
+ etch_url* newurl = (etch_url*) new_object(sizeof(etch_url), ETCHTYPEB_URL, CLASSID_URL);
+
+ ((etch_object*)newurl)->destroy = destroy_url;
+ ((etch_object*)newurl)->clone = clone_null;
+
+ newurl->raw = new_wchar(urlstring);
+ etchurl_parse(newurl, urlstring);
+
+ return newurl;
+}
+
+
+
+
+/**
+ * etchurl_get_params()
+ * @return an iterator over the URL parameters. caller owns and must destroy the iterator.
+ */
+etch_iterator* etchurl_get_params(etch_url* url)
+{
+ etch_iterator* iterator = url->params?
+ new_iterator(url->params, &url->params->iterable):
+ new_empty_iterator();
+
+ return iterator;
+}
+
+
+/**
+ * etchurl_paramcount()
+ * @return count of parameters present in the URL
+ */
+int etchurl_paramcount(etch_url* url)
+{
+ return url->params? url->params->count: 0;
+}
+
+
+/**
+ * etchurl_termcount()
+ * @return count of terms present in the URL
+ */
+int etchurl_termcount(etch_url* url)
+{
+ return etchmap_count(url->terms);
+}
+
+
+/**
+ * etchurl_parse()
+ * parse out a raw URL
+ */
+int etchurl_parse(etch_url* thisx, wchar_t* rawurl)
+{
+ wchar_t *q = 0, *p = 0, *urlcopy = 0, *ss = 0;
+ int result = -1;
+ if (NULL == rawurl) return -1;
+ urlcopy = new_wchar(rawurl); p = urlcopy;
+
+ do
+ { thisx->charcount = wcslen(p);
+ thisx->bytecount = sizeof(wchar_t) * thisx->charcount; /* minus nullterm */
+ if (0 == thisx->bytecount) break;
+
+ /* scheme:[//[user[:password]@]host[:port]/]uri[;params][?terms][#frag] */
+ ss = wcsstr(p, L"//");
+ q = wcschr(p, L':');
+
+ if (!ss || !q)
+ thisx->scheme = new_wchar(ETCH_URL_DEFAULT_SCHEME);
+ else
+ if (q && (ss > q))
+ { *q++ = L'\0';
+ thisx->scheme = etchurl_unescape(thisx, p);
+ p = q;
+ }
+ else break;
+
+ /* s is [//[user[:password]@]host[:port]/]uri[;params][?terms][#frag] */
+ if (NULL != (q = wcschr(p, L'#')))
+ { *q++ = L'\0';
+ thisx->fragment = etchurl_unescape(thisx, q);
+ }
+
+ /* [//[user[:password]@]host[:port]/]uri[;params][?terms] */
+ if (NULL != (q = wcschr(p, L'?')))
+ { *q++ = L'\0';
+ etchurl_parseterms(thisx, q);
+ }
+
+ /* [//[user[:password]@]host[:port]/]uri[;params] */
+ if (NULL != (q = wcschr(p, L';')))
+ { *q++ = L'\0';
+ etchurl_parseparams(thisx, q);
+ }
+
+ /* [//[user[:password]@]host[:port]/]uri */
+ if (p == wcsstr(p, L"//")) /* begins with "//"? */
+ { p++; p++;
+ }
+
+ /* [user[:password]@]host[:port]/]uri */
+ if (NULL != (q = wcschr(p, L'/'))) /* if slash found ... */
+ { /* [user[:password]@]host[:port]/uri */
+ *q++ = L'\0';
+ etchurl_parsehost(thisx, p);
+ p = q;
+ }
+ else
+ { /* [user[:password]@]host[:port] */
+ etchurl_parsehost(thisx, p);
+ *p = L'\0'; /* p now empty string */
+ }
+
+ thisx->uri = etchurl_unescape(thisx, p);
+
+ result = 0;
+
+ } while(0);
+
+ etch_free(urlcopy);
+ return result;
+}
+
+
+/**
+ * etchurl_parsepass()
+ */
+void etchurl_parsepass(etch_url* thisx, wchar_t* s)
+{
+ wchar_t *p = s, *q = 0;
+ /* user[:password] */
+
+ if (NULL != (q = wcschr(p, L':')))
+ { *q++ = L'\0';
+ thisx->password = etchurl_unescape(thisx, q);
+ }
+
+ thisx->user = etchurl_unescape(thisx, p);
+}
+
+
+/**
+ * etchurl_parseport()
+ */
+void etchurl_parseport(etch_url* thisx, wchar_t* s)
+{
+ wchar_t *p = s, *q = 0, *a = 0;
+ /* host[:port] */
+
+ if (NULL != (q = wcschr(p, L':')))
+ { *q++ = L'\0';
+ a = etchurl_unescape(thisx, q);
+ //thisx->port = _wtoi(a);
+#ifdef WIN32
+ thisx->port = _wtoi(a);
+#else
+ //TODO: check this
+ thisx->port = wcstol(a, NULL, 10);
+#endif
+
+ etch_free(a);
+ }
+
+ a = etchurl_unescape(thisx, p);
+ thisx->host = a;
+}
+
+
+/**
+ * etchurl_parsehost()
+ */
+void etchurl_parsehost(etch_url* thisx, wchar_t* s)
+{
+ wchar_t *p = s, *q = 0;
+
+ /* [user[:password]@]host[:port] */
+ if (NULL != (q = wcschr(p, L'@')))
+ { *q++ = L'\0';
+ etchurl_parsepass(thisx, p);
+ etchurl_parseport(thisx, q);
+ }
+ else etchurl_parseport(thisx, p);
+}
+
+
+/**
+ * etchurl_remove_term()
+ * remove term from term map, destroying term key string and returning term value object.
+ * @param key a raw string representing the key of the pair to be removed from the map.
+ * caller retains ownership of this string.
+ * @return the etch object value of the key/value pair removed from the map, or null.
+ * caller assumes ownership of this object.
+ */
+etch_object* etchurl_remove_term(etch_url* url, wchar_t* key)
+{
+ int result = 0;
+ etch_hashitem bucket, *removeditem = &bucket;
+ const int keylen = (int) wcslen(key) * sizeof(wchar_t);
+ memset(removeditem, 0, sizeof(etch_hashitem));
+
+ result = ((struct i_hashtable*)((etch_object*)url->terms)->vtab)->remove(url->terms->realtable,
+ key, keylen, url->terms, (void*)&removeditem);
+
+ etch_free(removeditem->key);
+
+ return removeditem->value;
+}
+
+
+/**
+ * etchurl_add_term()
+ * @param termname a disposable string, caller relinquishes ownership
+ * @param termval a disposable string, caller relinquishes ownership
+ * @return 0 or -1
+ */
+int etchurl_add_term(etch_url* url, wchar_t* termname, wchar_t* termval)
+{
+ int result = 0;
+ etch_string* newvalobj = new_stringw(termval);
+ etch_object* foundvalobj = etchmap_findxw (url->terms, termname, NULL);
+
+ /* zero is insertxw error return */
+ if (NULL == foundvalobj) {
+ result = etchmap_insertxw(url->terms, termname, newvalobj, FALSE)? 0: -1;
+ }
+ else
+ if(is_etch_set(foundvalobj)) {
+ /* -1 is set_add error return */
+ result = etchmap_set_add((etch_set*)foundvalobj, (etch_object*) newvalobj)? -1: 0;
+ }
+ else {
+ /* consolidate terms for this key */
+ etch_set* thisset = new_etch_term_consolidator_set();
+ etch_object* removedvalobj = NULL;
+
+ etchmap_set_add (thisset, foundvalobj);
+ etchmap_set_add (thisset, (etch_object*) newvalobj);
+
+ removedvalobj = etchurl_remove_term(url, termname);
+ ETCH_ASSERT(removedvalobj == foundvalobj);
+
+ /* replace the single term with the new term consolidator set */
+ result = etchmap_insertxw (url->terms, termname, thisset, FALSE)? 0: -1;
+ }
+
+ etch_free(termname);
+ etch_free(termval);
+ return result;
+}
+
+
+/**
+ * etchurl_add_double_term()
+ * todo: remove this if we don't use it
+ */
+int etchurl_add_double_term(etch_url* url, wchar_t* termname, const double termval)
+{
+ wchar_t buf[128], *newstr = NULL;
+ memset(buf, 0, sizeof(buf));
+ etch_snwprintf(buf, 127, L"%f", termval);
+ newstr = new_wchar(buf); /* we relinquish newstr to etchurl_add_term() */
+ return etchurl_add_term(url, termname, newstr);
+}
+
+
+/**
+ * etchurl_add_integer_term()
+ */
+int etchurl_add_integer_term(etch_url* url, wchar_t* termname, const int termval)
+{
+ wchar_t buf[36], *newstr = NULL;
+ memset(buf, 0, sizeof(buf));
+#ifdef WIN32
+ _itow(termval, buf, 10);
+#else
+ etch_snwprintf(buf, sizeof(buf), L"%d", termval);
+ //swprintf(buf, L"%d", termval);
+#endif
+ newstr = new_wchar(buf); /* we relinquish newstr to etchurl_add_term() */
+ return etchurl_add_term(url, termname, newstr);
+}
+
+
+/**
+ * etchurl_add_param()
+ * @param param a disposable string, caller relinquishes
+ */
+int etchurl_add_param(etch_url* url, wchar_t* param)
+{
+ if (etchurl_is_emptystring(param))
+ { etch_free(param);
+ return -1;
+ }
+
+ if (NULL == url->params) /* lazy-allocate param map */
+ url->params = new_etchurl_paramlist();
+
+ return etch_arraylist_add (url->params, new_stringw(param));
+}
+
+
+/**
+ * etchurl_unescape()
+ * @param s a string from which escapes are to be removed. caller retains ownership.
+ * @return a newly-allocated unescaped string for which caller assumes ownership.
+ */
+wchar_t* etchurl_unescape(etch_url* url, wchar_t* s)
+{
+ int n1, n2;
+ wchar_t *p = s, *q = 0, *newstr = 0, c0, c1, c2;
+ const size_t charlen = wcslen(s), bytecount = (charlen + 1) * sizeof(wchar_t);
+
+ newstr = etch_malloc(bytecount, ETCHTYPEB_STRING);
+ memset(newstr, 0, bytecount);
+ q = newstr;
+
+ while(*p)
+ {
+ switch(*p)
+ { case L'%':
+ if (c1 = *++p) break;
+ if (c2 = *++p) break;
+ n1 = hexwchar_to_int(c1);
+ n2 = hexwchar_to_int(c2);
+ c0 = (n1 << 4) | n2;
+ *q++ = c0;
+ break;
+
+ case L'+': *q++ = L' '; break;
+
+ default: *q++ = *p;
+ }
+
+ if (*p) ++p;
+ }
+
+ return newstr; /* caller owns */
+}
+
+
+/**
+ * etchurl_parseterm()
+ * @param s, a url string. caller retains ownership, however the string is modified herein.
+ * s content is expected as name[=value]
+ */
+int etchurl_parseterm(etch_url* url, wchar_t* s)
+{
+ wchar_t *p = s, *q = 0, *key = 0, *val = 0;
+ if (etchurl_is_emptystring(s)) return 0;
+
+ if (NULL == url->terms) /* lazy-allocate term map */
+ url->terms = new_etchurl_termmap();
+
+ q = wcschr(p, L'=');
+ if (q) *q = L'\0';
+
+ key = etchurl_unescape(url, p); /* acquire key memory */
+
+ if(q && !etchurl_is_emptystring(++q)) {
+ val = etchurl_unescape(url, q); /* acquire val memory */
+ etchurl_add_term(url, key, val); /* relinquish key&val */
+ }
+ else {
+ /* relinquish key */
+ etchurl_add_term(url, key, new_wchar(L""));
+ }
+
+ return 0;
+}
+
+
+/**
+ * etchurl_parseterms()
+ * @param s a string containing delimited URL terms.
+ * caller retains ownership of this string, however it is modified herein.
+ */
+int etchurl_parseterms(etch_url* url, wchar_t* s)
+{
+ wchar_t *p = s, *q = 0;
+
+ while(1)
+ { if (NULL == (q = wcschr(p, L'&'))) break;
+ *q = L'\0';
+ etchurl_parseterm(url, p);
+ p = ++q;
+ }
+
+ etchurl_parseterm(url, p);
+
+ return 0;
+}
+
+
+/**
+ * etchurl_parseparams()
+ * @param s a string containing delimited URL parameters.
+ * caller retains ownership of this string, however it is modified herein.
+ */
+int etchurl_parseparams(etch_url* url, wchar_t* s)
+{
+ wchar_t *p = s, *q = 0, *a = 0;
+ if (etchurl_is_emptystring(s)) return 0;
+
+ while(1)
+ { if (NULL == (q = wcschr(p, L';'))) break;
+ *q = L'\0';
+ a = etchurl_unescape(url, p); /* acquire string a */
+ etchurl_add_param(url, a); /* relinquish string a */
+ p = ++q;
+ }
+
+ if (!etchurl_is_emptystring(p)) /* clone final substring */
+ etchurl_add_param(url, new_wchar(p));
+
+ return 0;
+}
+
+
+/**
+ * etchurl_get_integer_term()
+ * returns integer value of specified query term in out parameter.
+ * @return 0 success, -1 not found or error.
+ */
+int etchurl_get_integer_term(etch_url* url, const wchar_t* termname, int* out)
+{
+ int result = 0;
+ etch_string* foundobj = (etch_string*) etchurl_get_term(url, termname);
+ if (!foundobj || !is_etch_string(foundobj) || !foundobj->char_count) return -1;
+
+#ifdef WIN32
+ result = _wtoi(foundobj->v.valw);
+#else
+ result = wcstol(foundobj->v.valw, NULL, 10);
+#endif
+
+ if(out) *out = result;
+
+ return result;
+}
+
+
+/**
+ * etchurl_get_boolean_term()
+ * this is not yet functional: see etchurl_get_integer_term()
+ */
+boolean etchurl_get_boolean_term(etch_url* url, const wchar_t* termname, boolean* retval)
+{
+ int result = 0;
+ etch_object* foundobj = etchurl_get_term(url, termname);
+
+ if (is_etch_boolean(foundobj))
+ if (retval)
+ *retval = ((etch_boolean*) foundobj)->value;
+ else;
+ else result = -1;
+
+ return result;
+}
+
+
+/**
+ * etchurl_get_term()
+ * returns a *reference* to a wrapped URL term. caller does not own it.
+ */
+etch_object* etchurl_get_term(etch_url* url, const wchar_t* termname)
+{
+ etch_object* founditem = NULL;
+
+ if (url->terms)
+ founditem = etchmap_findxw (url->terms, (wchar_t*) termname, NULL);
+
+ return founditem;
+}
+
+
+/**
+ * etchurl_is_emptystring()
+ */
+int etchurl_is_emptystring(wchar_t* s)
+{
+ wchar_t* p = s;
+ size_t charlen = s? wcslen(s): 0;
+ if (0 == charlen) return TRUE;
+ while(*p++) if (*p != L' ') return FALSE;
+ return TRUE;
+}
+
+
+/*
+ * etchurl_termmap_clear_handler()
+ * clear callback for term map.
+ */
+int etchurl_termmap_clear_handler (void* keyData, void* valueData)
+{
+ wchar_t* key = (wchar_t*)keyData;
+ etch_object* value = (etch_object*)valueData;
+ etch_free(key); /* free string key */
+ etch_object_destroy(value); /* free etch object value */
+ return TRUE;
+}
+
+
+/*
+ * etchurl_termset_clear_handler()
+ * clear callback for term consolidation set.
+ * since it is a set, it does not contains values, only keys which are etch objects
+ */
+int etchurl_termset_clear_handler (void* data, void* value)
+{
+ etch_object* key = (etch_object*)data;
+ ETCH_ASSERT(key);
+ etch_object_destroy(key);
+ return TRUE;
+}
+
+
+/*
+ * new_etch_term_consolidator_set()
+ * constructor for a set configured appropriately for use as a term consolidator.
+ * since it is a set, it does not contains values, only keys which are etch objects.
+ * the new set is configured such that these objects are owned by the set
+ * and will therefore be destroyed with the set.
+ */
+etch_set* new_etch_term_consolidator_set()
+{
+ etch_set* thisset = new_set(ETCH_URL_DEFSUBTERMS);
+ thisset->is_readonly_keys = FALSE;
+ thisset->freehook = etchurl_termset_clear_handler;
+ return thisset;
+}
+
+
+/*
+ * new_etch_urlparamlist()
+ * list of URL parameters. list entries are etch_string objects. the list is
+ * configured such that these objects are destroyed when the list is cleared.
+ */
+etch_arraylist* new_etchurl_paramlist()
+{
+ etch_arraylist* list = new_etch_arraylist(ETCH_URL_DEFNUMPARMS, 0);
+ list->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
+ list->content_obj_type = ETCHTYPEB_PRIMITIVE;
+ list->content_class_id = CLASSID_STRING;
+ list->is_readonly = FALSE;
+ return list;
+}
+
+
+/*
+ * new_etch_urltermmap()
+ * map of URL term name/value pairs.
+ * map keys are raw strings. map values are etch objects, which will be either
+ * etch_string*, or etch_set*. such a set consolidates objects for a given key.
+ * the map is configured such that content keys and values are destroyed when
+ * the map is destroyed.
+ */
+etch_hashtable* new_etchurl_termmap()
+{
+ etch_hashtable* terms = new_hashtable(ETCH_URL_DEFNUMTERMS);
+ terms->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+ terms->content_obj_type = ETCHTYPEB_PRIMITIVE;
+ terms->content_class_id = CLASSID_ANY;
+ terms->is_readonly_keys = terms->is_readonly_values = FALSE;
+ terms->freehook = etchurl_termmap_clear_handler; /* frees memory on clear */
+ return terms;
+}
+
+
+int is_url_scheme_http (etch_url* url) { return url && url->scheme && 0 == wcscmp(url->scheme, L"http"); }
+int is_url_scheme_tcp (etch_url* url) { return url && url->scheme && 0 == wcscmp(url->scheme, L"tcp"); }
+int is_url_scheme_udp (etch_url* url) { return url && url->scheme && 0 == wcscmp(url->scheme, L"udp"); }
+
+
+
+
diff --git a/binding-c/runtime/c/src/test/CUnit-Run.dtd b/binding-c/runtime/c/src/test/CUnit-Run.dtd
new file mode 100644
index 0000000..1e92a0a
--- /dev/null
+++ b/binding-c/runtime/c/src/test/CUnit-Run.dtd
@@ -0,0 +1,35 @@
+<!ELEMENT CUNIT_TEST_RUN_REPORT
+ (CUNIT_HEADER, CUNIT_RESULT_LISTING, CUNIT_RUN_SUMMARY, CUNIT_FOOTER)>
+
+<!ELEMENT CUNIT_HEADER EMPTY>
+
+<!ELEMENT CUNIT_RESULT_LISTING (CUNIT_RUN_SUITE*|CUNIT_RUN_GROUP*)>
+
+<!ELEMENT CUNIT_RUN_SUITE (CUNIT_RUN_SUITE_SUCCESS|CUNIT_RUN_SUITE_FAILURE)>
+ <!ELEMENT CUNIT_RUN_SUITE_SUCCESS (SUITE_NAME,CUNIT_RUN_TEST_RECORD*)>
+ <!ELEMENT CUNIT_RUN_SUITE_FAILURE (SUITE_NAME,FAILURE_REASON)>
+ <!ELEMENT SUITE_NAME (#PCDATA)>
+ <!ELEMENT FAILURE_REASON (#PCDATA)>
+
+<!ELEMENT CUNIT_RUN_GROUP (CUNIT_RUN_GROUP_SUCCESS|CUNIT_RUN_GROUP_FAILURE)>
+ <!ELEMENT CUNIT_RUN_GROUP_SUCCESS (GROUP_NAME,CUNIT_RUN_TEST_RECORD*)>
+ <!ELEMENT CUNIT_RUN_GROUP_FAILURE (GROUP_NAME,FAILURE_REASON)>
+ <!ELEMENT GROUP_NAME (#PCDATA)>
+
+<!ELEMENT CUNIT_RUN_TEST_RECORD (CUNIT_RUN_TEST_SUCCESS|CUNIT_RUN_TEST_FAILURE)>
+ <!ELEMENT CUNIT_RUN_TEST_SUCCESS (TEST_NAME)>
+ <!ELEMENT CUNIT_RUN_TEST_FAILURE (TEST_NAME, FILE_NAME, LINE_NUMBER, CONDITION)>
+ <!ELEMENT TEST_NAME (#PCDATA)>
+ <!ELEMENT FILE_NAME (#PCDATA)>
+ <!ELEMENT LINE_NUMBER (#PCDATA)>
+ <!ELEMENT CONDITION (#PCDATA)>
+
+<!ELEMENT CUNIT_RUN_SUMMARY (CUNIT_RUN_SUMMARY_RECORD*)>
+ <!ELEMENT CUNIT_RUN_SUMMARY_RECORD (TYPE, TOTAL, RUN, SUCCEEDED, FAILED)>
+ <!ELEMENT TYPE (#PCDATA)>
+ <!ELEMENT TOTAL (#PCDATA)>
+ <!ELEMENT RUN (#PCDATA)>
+ <!ELEMENT SUCCEEDED (#PCDATA)>
+ <!ELEMENT FAILED (#PCDATA)>
+
+<!ELEMENT CUNIT_FOOTER (#PCDATA)>
diff --git a/binding-c/runtime/c/src/test/CUnit-Run.xsl b/binding-c/runtime/c/src/test/CUnit-Run.xsl
new file mode 100644
index 0000000..74cb1e0
--- /dev/null
+++ b/binding-c/runtime/c/src/test/CUnit-Run.xsl
@@ -0,0 +1,152 @@
+<?xml version='1.0'?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+
+<xsl:stylesheet
+ version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <xsl:template match="CUNIT_TEST_RUN_REPORT">
+ <html>
+ <head>
+ <title> CUnit - All Test Run Summary Report </title>
+ </head>
+
+ <body bgcolor="#e0e0f0">
+ <xsl:apply-templates/>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_HEADER">
+ <div align="center">
+ <h3> <b> CUnit - A Unit testing framework for C. </b>
+ <br/> <a href="http://cunit.sourceforge.net/"> http://cunit.sourceforge.net/ </a> </h3>
+ </div>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RESULT_LISTING">
+ <table cols="4" width="90%" align="center">
+ <tr>
+ <td width="25%"> </td>
+ <td width="25%"> </td>
+ <td width="25%"> </td>
+ <td width="25%"> </td>
+ </tr>
+ <xsl:apply-templates/>
+ </table>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RUN_SUITE">
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <xsl:template match="SUITE_NAME">
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RUN_SUITE_SUCCESS">
+ <tr bgcolor="#f0e0f0">
+ <td colspan="4"> Running Suite <xsl:value-of select="SUITE_NAME"/> </td>
+ </tr>
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RUN_GROUP">
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RUN_GROUP_SUCCESS">
+ <tr bgcolor="#f0e0f0">
+ <td colspan="4"> Running Group <xsl:apply-templates/> </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RUN_TEST_RECORD">
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RUN_TEST_SUCCESS">
+ <tr bgcolor="#e0f0d0">
+ <td> </td> <td colspan="2"> Running test <xsl:apply-templates/>... </td> <td bgcolor="#50ff50"> Passed </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RUN_TEST_FAILURE">
+ <tr bgcolor="#e0f0d0">
+ <td> </td> <td colspan="2"> Running test <xsl:value-of select="TEST_NAME"/>... </td> <td bgcolor="#ff5050"> Failed </td>
+ </tr>
+
+ <tr>
+ <td colspan="4" bgcolor="#ff9090">
+ <table width="100%">
+ <tr> <th width="15%"> File Name </th> <td width="50%" bgcolor="#e0eee0"> <xsl:value-of select="FILE_NAME"/> </td> <th width="20%"> Line Number </th> <td width="10%" bgcolor="#e0eee0"> <xsl:value-of select="LINE_NUMBER"/> </td> </tr>
+ <tr> <th width="15%"> Condition </th> <td colspan="3" width="85%" bgcolor="#e0eee0"> <xsl:value-of select="CONDITION"/> </td> </tr>
+ </table>
+ </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RUN_SUITE_FAILURE">
+ <tr>
+ <td colspan="3" bgcolor="#f0b0f0">Running Suite <xsl:value-of select="SUITE_NAME"/>... </td>
+ <td bgcolor="#ff7070"> <xsl:value-of select="FAILURE_REASON"/> </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RUN_GROUP_FAILURE">
+ <tr>
+ <td colspan="3" bgcolor="#f0b0f0">Running Group <xsl:value-of select="GROUP_NAME"/>... </td>
+ <td bgcolor="#ff7070"> <xsl:value-of select="FAILURE_REASON"/> </td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_RUN_SUMMARY">
+ <p/>
+ <table width="90%" rows="5" align="center">
+ <tr align="center" bgcolor="skyblue"> <th colspan="5"> Cumulative Summary for Run </th> </tr>
+ <tr>
+ <th width="20%" bgcolor="#ffffc0" align="center"> Type </th>
+ <th width="20%" bgcolor="#ffffc0" align="center"> Total </th>
+ <th width="20%" bgcolor="#ffffc0" align="center"> Run </th>
+ <th width="20%" bgcolor="#ffffc0" align="center"> Succeeded </th>
+ <th width="20%" bgcolor="#ffffc0" align="center"> Failed </th>
+ </tr>
+ <xsl:for-each select="CUNIT_RUN_SUMMARY_RECORD">
+ <tr align="center" bgcolor="lightgreen">
+ <td> <xsl:value-of select="TYPE" /> </td>
+ <td> <xsl:value-of select="TOTAL" /> </td>
+ <td> <xsl:value-of select="RUN" /> </td>
+ <td> <xsl:value-of select="SUCCEEDED" /> </td>
+ <td> <xsl:value-of select="FAILED" /> </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:template>
+
+ <xsl:template match="CUNIT_FOOTER">
+ <p/>
+ <hr align="center" width="90%" color="red" />
+ <h5 align="center">
+ <xsl:apply-templates/>
+ </h5>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/binding-c/runtime/c/src/test/common/test_arraylist.c b/binding-c/runtime/c/src/test/common/test_arraylist.c
new file mode 100644
index 0000000..8cedfc1
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_arraylist.c
@@ -0,0 +1,681 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_arraylist.c -- test etch_arraylist
+ */
+#include "etch_runtime.h"
+#include "etch_arraylist.h"
+#include "etch_thread.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define OBJSIG 0xbadf00d
+#define NUMITEMS 3
+#define ETCHTYPEA_TESTOBJ 0xff
+#define IS_DEBUG_CONSOLE FALSE
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ return etch_status;
+}
+
+static int clean_suite(void)
+{
+ return etch_runtime_shutdown();
+}
+
+typedef struct TESTOBJ
+{ int id;
+ int signature;
+} TESTOBJ;
+
+
+static TESTOBJ* objfactory(const int id)
+{
+ TESTOBJ* obj = etch_malloc(sizeof(TESTOBJ), ETCHTYPEA_TESTOBJ);
+ obj->id = id; obj->signature = OBJSIG;
+ return obj;
+}
+
+
+/**
+ * comparator callback for contains(), indexof(), functions
+ * typedef int (*etch_comparator) (void* myobj, void* otherobj);
+ */
+static int al_comparator(void* p, void* q)
+{
+ int result = 0;
+ TESTOBJ* myobj = (TESTOBJ*)p, *othobj = (TESTOBJ*)q;
+ if (!myobj || !othobj) result = -2;
+ else if (myobj->signature != OBJSIG || othobj->signature != OBJSIG) result = -2;
+ else if (myobj->id < othobj->id) result = -1;
+ else if (myobj->id > othobj->id) result = 1;
+ else result = 0; /* equality */
+ return result;
+}
+
+
+/*
+ * test new_arraylist()
+ */
+static void test_new_arraylist(void)
+{
+ const int TESTSIZE = 2048, TESTDELTA = 1024;
+
+ // check default size
+ etch_arraylist* list = new_etch_arraylist(0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(list);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(list->base);
+ CU_ASSERT_EQUAL(list->count,0);
+ CU_ASSERT_EQUAL(list->size, ETCHARRAYLIST_DEFSIZE * sizeof(void**));
+ CU_ASSERT_EQUAL(list->delta, ETCHARRAYLIST_DEFSIZE * sizeof(void**));
+
+ //free memory and ensure nothing remains allocated
+ etch_arraylist_destroy(list,TRUE);
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(TRUE,FALSE);
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+#endif
+
+ // check custome size
+ list = new_etch_arraylist(TESTSIZE, TESTDELTA);
+ CU_ASSERT_EQUAL(list->count,0);
+ CU_ASSERT_EQUAL(list->size, TESTSIZE * sizeof(void**));
+ CU_ASSERT_EQUAL(list->delta, TESTDELTA * sizeof(void**));
+ memset(list->base, 0xff, list->size); /* try write to entire buffer */
+
+ // free memory; ensure nothing remains allocated
+ etch_arraylist_destroy(list,TRUE);
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(TRUE,FALSE);
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test arraylist_add()
+ */
+static void test_add(void)
+{
+ TESTOBJ *x1, *x2;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+ CU_ASSERT_EQUAL(list->base[0],NULL);
+ CU_ASSERT_EQUAL(list->base[1],NULL);
+
+ etch_arraylist_add(list, x1 = objfactory(1));
+ CU_ASSERT_EQUAL(list->count,1);
+ CU_ASSERT_EQUAL(list->base[0], x1); /* ensure item's buffer slot OK */
+
+ etch_arraylist_add(list, x2 = objfactory(2));
+ CU_ASSERT_EQUAL(list->count,2);
+ CU_ASSERT_EQUAL(list->base[1], x2); /* ensure item's buffer slot OK */
+
+ // free memory including test objects
+ etch_arraylist_destroy(list,TRUE);
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(TRUE,IS_DEBUG_CONSOLE);
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test arraylist_destroy()
+ */
+static void test_destroy(void)
+{
+ CU_PASS("tested in all previous and subsequent tests");
+}
+
+/*
+ * test arraylist_count()
+ */
+static void test_count(void)
+{
+ TESTOBJ *x1, *x2;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+ CU_ASSERT_EQUAL(list->count,0);
+ CU_ASSERT_EQUAL(etch_arraylist_count(list),0);
+
+ etch_arraylist_add(list, x1 = objfactory(1));
+ CU_ASSERT_EQUAL(list->count,1);
+ CU_ASSERT_EQUAL(etch_arraylist_count(list),1);
+
+ etch_arraylist_add(list, x2 = objfactory(2));
+ CU_ASSERT_EQUAL(list->count,2);
+ CU_ASSERT_EQUAL(etch_arraylist_count(list),2);
+
+ // free memory including test objects
+ etch_arraylist_destroy(list,TRUE);
+ #ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(TRUE,IS_DEBUG_CONSOLE);
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+ #endif
+}
+
+/*
+ * test arraylist_remove()
+ */
+static void test_remove_firstitem(void)
+{
+ TESTOBJ *x1, *x2;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+
+ etch_arraylist_add(list, x1 = objfactory(1)); /* allocate and add test obj x1 */
+ etch_arraylist_add(list, x2 = objfactory(2)); /* allocate and add test obj x2 */
+
+ CU_ASSERT_EQUAL_FATAL(list->count,2);
+ CU_ASSERT_EQUAL(list->base[0], x1);
+ CU_ASSERT_EQUAL(list->base[1], x2);
+
+ //remove list[0] and free its x1 memory
+ etch_arraylist_remove(list,0,TRUE);
+
+ CU_ASSERT_EQUAL_FATAL(list->count,1);
+
+ // ensure TESTBOJ bytes were freed
+ // ensure item's buffer slot cleared
+ CU_ASSERT_EQUAL(list->base[1], NULL);
+
+ // ensure item 1 is now item 0
+ CU_ASSERT_EQUAL_FATAL(list->base[0], x2);
+
+ // remove list[0] but don't free x2 memory
+ etch_arraylist_remove(list,0,FALSE);
+
+ CU_ASSERT_EQUAL(list->count,0); /* ensure no bytes were freed */
+
+ // ensure item's buffer slot cleared
+ CU_ASSERT_EQUAL(list->base[0], NULL);
+
+ /* finally free the last test object, which would have been freed in the
+ * prior arraylist_remove() had we not specified to not do so. Also if we
+ * did not free it here it would be freed in the memtable_clear() below.
+ */
+ etch_free(x2);
+
+ // destroy the arraylist
+ etch_arraylist_destroy(list,TRUE);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+/*
+ * test arraylist_remove()
+ */
+static void test_remove_lastitem(void)
+{
+ TESTOBJ *x1, *x2, *x3;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+
+ etch_arraylist_add(list, x1 = objfactory(1)); /* allocate and add test obj x1 */
+ etch_arraylist_add(list, x2 = objfactory(2)); /* allocate and add test obj x2 */
+ etch_arraylist_add(list, x3 = objfactory(3)); /* allocate and add test obj x3 */
+ CU_ASSERT_EQUAL_FATAL(list->count,3);
+
+ CU_ASSERT_EQUAL(list->base[2], x3);
+
+ // remove list[2] and free its x3 memory
+ etch_arraylist_remove(list,2,TRUE);
+
+ CU_ASSERT_EQUAL(list->base[2], NULL); /* ensure item's buffer slot cleared */
+
+ CU_ASSERT_EQUAL_FATAL(list->base[0], x1);
+ CU_ASSERT_EQUAL_FATAL(list->base[1], x2);
+
+ /* destroy the arraylist freeing the test object content as well */
+ etch_arraylist_destroy(list,TRUE);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+/*
+static void dumplist(etch_arraylist* list)
+{
+ int i=0; char c=0;
+ const int n = list->count;
+ printf("\ndumping list ...\n"); fflush(stdout);
+ for(; i < n; i++)
+ { TESTOBJ* item = list->base[i];
+ printf("\n list[%d] %d\n", i, item->id); fflush(stdout);
+ }
+
+ printf("\nany key ..."); while(!c) c = getchar(); printf("\n");
+}
+*/
+
+/*
+ * removetest_comparator()
+ * comparator for test_remove() arraylist_indexof
+ * note that the test value is passed as an int not an object
+ */
+static int removetest_comparator(void* testvalue, void* listcontent)
+{
+ const int ivalue = (int) (size_t) testvalue;
+ TESTOBJ* listobj = (TESTOBJ*) listcontent;
+ int jvalue = listobj->id;
+ return ivalue < jvalue? -1: ivalue > jvalue? 1: 0;
+}
+
+
+/*
+ * test_remove()
+ * remove items
+ */
+static void test_remove(void)
+{
+ const int numitems = 10;
+ int i=0, curritems = numitems;
+ etch_arraylist* list = new_etch_arraylist(64,0);
+
+ for(; i < numitems; i++)
+ etch_arraylist_add(list, objfactory(i));
+
+ #if IS_DEBUG_CONSOLE
+ dumplist(list);
+ #endif
+
+ for(i=2; i < numitems; i+=2)
+ {
+ /* remove the list content whose value is i */
+ const int index = etch_arraylist_indexof(list, (void*)(size_t) i, 0, removetest_comparator);
+ CU_ASSERT_NOT_EQUAL(index, -1);
+
+ if (index >= 0)
+ {
+ if (0 == etch_arraylist_remove(list, index, TRUE))
+ curritems--;
+
+ #if IS_DEBUG_CONSOLE
+ dumplist(list);
+ #endif
+ }
+ else
+ {
+ #if IS_DEBUG_CONSOLE
+ printf("could not remove list content %d\n", i);
+ #endif
+ }
+
+ CU_ASSERT_EQUAL(list->count, curritems);
+ }
+
+ /* destroy the arraylist freeing the test object content as well */
+ etch_arraylist_destroy(list,TRUE);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test arraylist_insert()
+ */
+static void test_insert_first(void)
+{
+ TESTOBJ *x1, *x2, *x3;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+
+ etch_arraylist_add(list, x2 = objfactory(2)); /* allocate and add test obj x2 */
+ etch_arraylist_add(list, x3 = objfactory(3)); /* allocate and add test obj x3 */
+
+ /* insert test obj x1 into slot 0 */
+ etch_arraylist_insert(list, 0, x1 = objfactory(1));
+
+ CU_ASSERT_EQUAL_FATAL(list->count,3);
+ CU_ASSERT_EQUAL(list->base[0], x1);
+ CU_ASSERT_EQUAL(list->base[1], x2);
+ CU_ASSERT_EQUAL(list->base[2], x3);
+
+ /* destroy the arraylist freeing the test object content as well */
+ etch_arraylist_destroy(list,TRUE);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test arraylist_insert()
+ */
+static void test_insert_mid(void)
+{
+ TESTOBJ *x1, *x2, *x3;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+
+ etch_arraylist_add(list, x1 = objfactory(1));
+ etch_arraylist_add(list, x3 = objfactory(3));
+
+ /* insert test obj x1 into slot 1 */
+ etch_arraylist_insert(list, 1, x2 = objfactory(2));
+
+ CU_ASSERT_EQUAL_FATAL(list->count,3);
+ CU_ASSERT_EQUAL(list->base[0], x1);
+ CU_ASSERT_EQUAL(list->base[1], x2);
+ CU_ASSERT_EQUAL(list->base[2], x3);
+
+ /* destroy the arraylist freeing the test object content as well */
+ etch_arraylist_destroy(list,TRUE);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test arraylist_insert()
+ */
+static void test_insert_end(void)
+{
+ TESTOBJ *x1, *x2, *x3;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+
+ etch_arraylist_add(list, x1 = objfactory(1));
+ etch_arraylist_add(list, x2 = objfactory(2));
+
+ /* insert test obj x1 into slot 1 */
+ etch_arraylist_insert(list, 2, x3 = objfactory(3));
+
+ CU_ASSERT_EQUAL_FATAL(list->count,3);
+ CU_ASSERT_EQUAL(list->base[0], x1);
+ CU_ASSERT_EQUAL(list->base[1], x2);
+ CU_ASSERT_EQUAL(list->base[2], x3);
+
+ /* destroy the arraylist freeing the test object content as well */
+ etch_arraylist_destroy(list,TRUE);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test arraylist_containsp(), arraylist_contains()
+ */
+static void test_contains(void)
+{
+ TESTOBJ *x1, *x2, *x3;
+ int result = 0;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+
+ etch_arraylist_add(list, x1 = objfactory(1));
+ etch_arraylist_add(list, x2 = objfactory(2));
+ etch_arraylist_add(list, x3 = objfactory(3));
+
+ result = etch_arraylist_containsp(list, x1, 0);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ result = etch_arraylist_containsp(list, x2, 0);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ result = etch_arraylist_containsp(list, x3, 0);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ result = etch_arraylist_containsp(list, x1, 1); /* start at index 1 */
+ CU_ASSERT_EQUAL(result, FALSE);
+
+ result = etch_arraylist_contains(list, x1, 0, al_comparator);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ result = etch_arraylist_contains(list, x2, 0, al_comparator);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ result = etch_arraylist_contains(list, x3, 0, al_comparator);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ result = etch_arraylist_contains(list, x1, 2, al_comparator); /* start at index 2 */
+ CU_ASSERT_EQUAL(result, FALSE);
+
+ /* destroy the arraylist freeing the test object content as well */
+ etch_arraylist_destroy(list,TRUE);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+/*
+ * test arraylist_indexofp(), arraylist_indexof()
+ */
+static void test_indexof(void)
+{
+ TESTOBJ *x1, *x2, *x3;
+ int result = 0;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+ x3 = objfactory(3);
+
+ etch_arraylist_add(list, x1 = objfactory(1));
+ etch_arraylist_add(list, x2 = objfactory(2));
+
+ result = etch_arraylist_indexofp(list, x1, 0);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = etch_arraylist_indexofp(list, x2, 0);
+ CU_ASSERT_EQUAL(result, 1);
+
+ result = etch_arraylist_indexofp(list, x3, 0);
+ CU_ASSERT_EQUAL(result, -1); /* not found */
+
+ result = etch_arraylist_indexofp(list, x1, 1); /* start at index 1 */
+ CU_ASSERT_EQUAL(result, -1);
+
+ result = etch_arraylist_indexof(list, x1, 0, al_comparator);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = etch_arraylist_indexof(list, x2, 0, al_comparator);
+ CU_ASSERT_EQUAL(result, 1);
+
+ result = etch_arraylist_indexof(list, x3, 0, al_comparator);
+ CU_ASSERT_EQUAL(result, -1);
+
+ result = etch_arraylist_indexof(list, x1, 1, al_comparator);
+ CU_ASSERT_EQUAL(result, -1);
+
+ etch_free(x3);
+
+ /* destroy the arraylist freeing the test object content as well */
+ etch_arraylist_destroy(list,TRUE);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test arraylist_get()
+ */
+static void test_get(void)
+{
+ TESTOBJ *x1, *x2, *x3, *x;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+
+ etch_arraylist_add(list, x1 = objfactory(1));
+ etch_arraylist_add(list, x2 = objfactory(2));
+ etch_arraylist_add(list, x3 = objfactory(3));
+
+ x = etch_arraylist_get(list,0);
+ CU_ASSERT_EQUAL(x, x1);
+ CU_ASSERT_EQUAL(((TESTOBJ*)x)->signature, OBJSIG);
+ CU_ASSERT_EQUAL(((TESTOBJ*)x)->id, 1);
+
+ x = etch_arraylist_get(list,1);
+ CU_ASSERT_EQUAL(x, x2);
+ CU_ASSERT_EQUAL(((TESTOBJ*)x)->signature, OBJSIG);
+ CU_ASSERT_EQUAL(((TESTOBJ*)x)->id, 2);
+
+ x = etch_arraylist_get(list,2);
+ CU_ASSERT_EQUAL(x, x3);
+ CU_ASSERT_EQUAL(((TESTOBJ*)x)->signature, OBJSIG);
+ CU_ASSERT_EQUAL(((TESTOBJ*)x)->id, 3);
+
+ x = etch_arraylist_get(list,3);
+ CU_ASSERT_EQUAL(x, NULL);
+
+ /* destroy the arraylist freeing the test object content as well */
+ etch_arraylist_destroy(list,TRUE);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test arraylist_set()
+ */
+static void test_set(void)
+{
+ int result = 0;
+ TESTOBJ *x1, *x2, *x3, *x4, *x5;
+ etch_arraylist* list = new_etch_arraylist(8,0);
+
+ etch_arraylist_add(list, x1 = objfactory(1));
+ etch_arraylist_add(list, x2 = objfactory(2));
+ etch_arraylist_add(list, x3 = objfactory(3));
+
+ result = etch_arraylist_set(list, 0, x4 = objfactory(4));
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(list->base[0], x4);
+
+ result = etch_arraylist_set(list, 2, x5 = objfactory(5));
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(list->base[2], x5);
+
+ result = etch_arraylist_set(list, 3, x5); /* attempt replace beyond end of list */
+ CU_ASSERT_EQUAL(result,-1);
+
+ /* destroy the arraylist without freeing the test object content */
+ etch_arraylist_destroy(list,FALSE);
+
+ etch_free(x1); etch_free(x2); etch_free(x3); etch_free(x4); etch_free(x5);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test arraylist auto reallocation
+ */
+static void test_expand(void)
+{
+ const int INIT_MAX = 4;
+ TESTOBJ *x1, *x2, *x3, *x4, *x5, *x6, *x;
+ etch_arraylist* list = new_etch_arraylist(INIT_MAX,0);
+
+ etch_arraylist_add(list, x1 = objfactory(1));
+ etch_arraylist_add(list, x2 = objfactory(2));
+ etch_arraylist_add(list, x3 = objfactory(3));
+ etch_arraylist_add(list, x4 = objfactory(4));
+ CU_ASSERT_EQUAL(list->size, INIT_MAX * sizeof(void**));
+
+ etch_arraylist_add(list, x5 = objfactory(5));
+ CU_ASSERT_EQUAL(list->size, INIT_MAX * sizeof(void**) * 2);
+ etch_arraylist_add(list, x6 = objfactory(6));
+
+ x = etch_arraylist_get(list,4);
+ CU_ASSERT_EQUAL(x, x5);
+ CU_ASSERT_EQUAL(((TESTOBJ*)x)->signature, OBJSIG);
+ CU_ASSERT_EQUAL(((TESTOBJ*)x)->id, 5);
+ x = etch_arraylist_get(list,5);
+ CU_ASSERT_EQUAL(x, x6);
+
+ /* destroy the arraylist freeing the test object content as well */
+ etch_arraylist_destroy(list,TRUE);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+CU_pSuite test_arraylist_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("suite_arraylist", init_suite, clean_suite);
+ CU_add_test(pSuite, "test new_arraylist()", test_new_arraylist);
+ CU_add_test(pSuite, "test arraylist_add()", test_add);
+ CU_add_test(pSuite, "test arraylist_destroy()", test_destroy);
+ CU_add_test(pSuite, "test arraylist_count()", test_count);
+ CU_add_test(pSuite, "test arraylist_remove() first", test_remove_firstitem);
+ CU_add_test(pSuite, "test arraylist_remove() last", test_remove_lastitem);
+ CU_add_test(pSuite, "test arraylist_remove() mid", test_remove);
+ CU_add_test(pSuite, "test arraylist_insert() first", test_insert_first);
+ CU_add_test(pSuite, "test arraylist_insert() mid", test_insert_mid);
+ CU_add_test(pSuite, "test arraylist_insert() end", test_insert_end);
+ CU_add_test(pSuite, "test arraylist_contains(), containsp", test_contains);
+ CU_add_test(pSuite, "test arraylist_indexof(), indexofp", test_indexof);
+ CU_add_test(pSuite, "test arraylist_get()", test_get);
+ CU_add_test(pSuite, "test arraylist set", test_set);
+ CU_add_test(pSuite, "test arraylist auto realloc", test_expand);
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_cache.c b/binding-c/runtime/c/src/test/common/test_cache.c
new file mode 100644
index 0000000..c8e64af
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_cache.c
@@ -0,0 +1,271 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_cache.c
+ * test the runtime object cache
+ * we can swap out cache back ends and this test should work the same regardless
+ */
+#include "etch_runtime.h"
+#include "etch_cache.h"
+#include "etch_arraylist.h"
+#include "etch_hash.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+
+#define IS_DEBUG_CONSOLE FALSE
+
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ return etch_runtime_initialize(NULL);
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+
+static int test_cache_freehook_func(void* key, void* value)
+{
+ //etch_free(key);
+ //if(value) {
+ // etch_free(value);
+ //}
+ return 0;
+}
+
+/**
+ * This subtest instantiates various etch objects which cache some part of
+ * themselves, and destroys the objects. At each step the test verifies that
+ * the cache contains the expected number of entries, e.g. if I create
+ * multiple hashtables I should only have cached one hashtable vtable.
+ */
+static void test_multiple_items(void)
+{
+ int cache_start_count = 0, cache_current_count;
+ int result1 = 0, result2 = 0, result3 = 0;
+ etch_hashtable* myhashtab1 = NULL;
+ etch_hashtable* myhashtab2 = NULL;
+ etch_hashtable* myhashtab3 = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* myentry = &hashbucket;
+
+ wchar_t* wstr1 = L"abracadabra";
+ wchar_t* wstr2 = L"gilgamesh";
+ wchar_t* wstr3 = L"antidisestablishmentarianism";
+
+ const size_t numElements1 = wcslen(wstr1);
+ const size_t numElements2 = wcslen(wstr2);
+ const size_t numElements3 = wcslen(wstr3);
+
+ const size_t numBytes1 = sizeof(wchar_t) * numElements1;
+ const size_t numBytes2 = sizeof(wchar_t) * numElements2;
+ const size_t numBytes3 = sizeof(wchar_t) * numElements3;
+
+ size_t actlen1 = 0, actlen2 = 0, actlen3 = 0;
+ wchar_t *key1 = NULL, *key2 = NULL, *key3 = NULL;
+
+ // set freehook for cache
+ etch_cache_set_freehook(test_cache_freehook_func);
+
+ key1 = etch_malloc(numBytes1 + sizeof(wchar_t), 0);
+ key2 = etch_malloc(numBytes2 + sizeof(wchar_t), 0);
+ key3 = etch_malloc(numBytes3 + sizeof(wchar_t), 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(key1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(key2);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(key3);
+
+ /* create one hashtable first, so in case we are tracking memory, we ensure that
+ * the hashtable code module paths will already be cached. */
+ myhashtab1 = new_hashtable(16);
+
+ cache_start_count = etch_cache_count();
+
+#if defined(WIN32) && !defined(_WIN32_WCE)
+ result1 = wcscpy_s(key1, numElements1+1, wstr1); /* wcscpy_s param 2 must be */
+ result2 = wcscpy_s(key2, numElements2+1, wstr2); /* number of characters + 1 */
+ result3 = wcscpy_s(key3, numElements3+1, wstr3);
+#elif defined(_WIN32_WCE)
+ wcsncpy(key1, wstr1, numElements1);
+ wcsncpy(key2, wstr2, numElements2);
+ wcsncpy(key3, wstr3, numElements3);
+#else
+ result1 = wcscpy(key1, wstr1);
+ result2 = wcscpy(key2, wstr2);
+ result3 = wcscpy(key3, wstr3);
+#endif
+ actlen1 = wcslen(key1);
+ actlen2 = wcslen(key2);
+ actlen3 = wcslen(key3);
+
+ myhashtab2 = new_hashtable(16);
+ myhashtab3 = new_hashtable(16);
+
+ /* we should not have cached any more hashtable vtables */
+ cache_current_count = etch_cache_count();
+ CU_ASSERT_EQUAL(cache_current_count, cache_start_count);
+
+ ((struct i_hashtable*)((etch_object*)myhashtab1)->vtab)->insert(myhashtab1->realtable, key1, (int)numBytes1, NULL,0,0,0);
+ ((struct i_hashtable*)((etch_object*)myhashtab2)->vtab)->insert(myhashtab2->realtable, key2, (int)numBytes2, NULL,0,0,0);
+ ((struct i_hashtable*)((etch_object*)myhashtab2)->vtab)->insert(myhashtab3->realtable, key3, (int)numBytes3, NULL,0,0,0);
+
+ /* TODO instantiate some other object here which uses the cache */
+
+ destroy_hashtable(myhashtab1, TRUE, TRUE);
+ destroy_hashtable(myhashtab2, TRUE, TRUE);
+ destroy_hashtable(myhashtab3, TRUE, TRUE);
+ /* note that key1 and key2 are now dangling pointers since we asked the
+ * hashtable to free keys and values memory
+ */
+ etch_free(key1);
+ etch_free(key2);
+ etch_free(key3);
+
+}
+
+
+/**
+ * test_intkeys()
+ * tests caching using integer keys as we might do for etchobjects such as vtables
+ */
+static void test_intkeys(void)
+{
+ int i, startsize, size;
+ const int STARTKEY = 0, ENDKEY = 512, KEYCOUNT = ENDKEY - STARTKEY;
+ char* teststring = "it works!";
+ char* item = etch_malloc(sizeof(teststring),0);
+ memcpy(item, teststring, sizeof(teststring));
+
+ // set freehook for cache
+ etch_cache_set_freehook(test_cache_freehook_func);
+
+ startsize = etch_cache_count();
+
+ for(i = STARTKEY; i < ENDKEY; i++)
+ etch_cache_add(i, item);
+
+ size = etch_cache_count();
+ CU_ASSERT_EQUAL(size, KEYCOUNT + startsize);
+
+ for(i = STARTKEY; i < ENDKEY; i++)
+ CU_ASSERT_PTR_NOT_NULL(etch_cache_find(i, 0));
+
+ for(i = STARTKEY; i < ENDKEY; i++)
+ CU_ASSERT_PTR_NOT_NULL(etch_cache_del(i));
+
+ size = etch_cache_count();
+ CU_ASSERT_EQUAL(size, startsize);
+
+ etch_free(item);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_intkeys()
+ * tests caching using string keys with no values, as we might do for source
+ * file paths in the debug allocator
+ */
+static void test_pathkeys(void)
+{
+ char* path1 = "..\\..\\foo\\bar\\file1.dat";
+ char* path2 = "..\\..\\foo\\bar\\file2.dat";
+ char* path3 = "c:\\the\\quick\\brown\\fox\\jumped\\over\\the\\lazy\\dog\\file3.dat";
+ unsigned hash1 = 0, hash2 = 0, hash3 = 0;
+ char* namefound = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* thisitem = &hashbucket;
+ int result = 0;
+ int len1 = (int)strlen(path1), len2 = (int)strlen(path2), len3 = (int)strlen(path3);
+
+ // set freehook for cache
+ etch_cache_set_freehook(test_cache_freehook_func);
+
+ hash1 = etch_cache_insertx (path1, NULL, FALSE);
+ hash2 = etch_cache_insertx (path2, NULL, FALSE);
+ hash3 = etch_cache_insertx (path3, NULL, FALSE);
+
+ memset(thisitem, 0, sizeof(etch_hashitem));
+ etch_cache_findx(path1, &thisitem);
+ CU_ASSERT_PTR_NOT_NULL(thisitem->key);
+ CU_ASSERT_EQUAL(hash1, thisitem->hash);
+ result = strncmp(path1, thisitem->key, len1);
+ CU_ASSERT_EQUAL(result,0);
+
+ memset(thisitem, 0, sizeof(etch_hashitem));
+ etch_cache_findx(path2, &thisitem);
+ CU_ASSERT_PTR_NOT_NULL(thisitem->key);
+ CU_ASSERT_EQUAL(hash2, thisitem->hash);
+ result = strncmp(path2, thisitem->key, len2);
+ CU_ASSERT_EQUAL(result,0);
+
+ memset(thisitem, 0, sizeof(etch_hashitem));
+ etch_cache_findx(path3, &thisitem);
+ CU_ASSERT_PTR_NOT_NULL(thisitem->key);
+ CU_ASSERT_EQUAL(hash3, thisitem->hash);
+ result = strncmp(path3, thisitem->key, len3);
+ CU_ASSERT_EQUAL(result,0);
+
+ memset(thisitem, 0, sizeof(etch_hashitem));
+ etch_cache_find_by_hash(hash1, &thisitem);
+ CU_ASSERT_PTR_NOT_NULL(thisitem->key);
+ result = strncmp(path1, thisitem->key, len1);
+ CU_ASSERT_EQUAL(result,0);
+
+ memset(thisitem, 0, sizeof(etch_hashitem));
+ etch_cache_find_by_hash(hash2, &thisitem);
+ CU_ASSERT_PTR_NOT_NULL(thisitem->key);
+ result = strncmp(path2, thisitem->key, len2);
+ CU_ASSERT_EQUAL(result,0);
+
+ memset(thisitem, 0, sizeof(etch_hashitem));
+ etch_cache_find_by_hash(hash3, &thisitem);
+ CU_ASSERT_PTR_NOT_NULL(thisitem->key);
+ result = strncmp(path3, thisitem->key, len3);
+ CU_ASSERT_EQUAL(result,0);
+}
+
+CU_pSuite test_cache_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("etch_cache", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test path strings as keys", test_pathkeys);
+ CU_add_test(pSuite, "multiple of same object test", test_multiple_items);
+ CU_add_test(pSuite, "integer cache key test", test_intkeys);
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_etch.c b/binding-c/runtime/c/src/test/common/test_etch.c
new file mode 100644
index 0000000..a0ca12c
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_etch.c
@@ -0,0 +1,76 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_etch.c -- test etch common
+ */
+#include "etch.h"
+
+#include <stdio.h>
+#include <wchar.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ return 0;
+}
+
+//**********************
+// START testcases here
+//**********************
+
+static void test_etch_datatypes(void)
+{
+ CU_ASSERT_EQUAL(sizeof(char), 1);
+ CU_ASSERT_EQUAL(sizeof(byte), 1);
+ CU_ASSERT_EQUAL(sizeof(int8), 1);
+ CU_ASSERT_EQUAL(sizeof(uint8), 1);
+ CU_ASSERT_EQUAL(sizeof(int16), 2);
+ CU_ASSERT_EQUAL(sizeof(uint16), 2);
+ CU_ASSERT_EQUAL(sizeof(int32), 4);
+ CU_ASSERT_EQUAL(sizeof(uint32), 4);
+ CU_ASSERT_EQUAL(sizeof(int64), 8);
+ CU_ASSERT_EQUAL(sizeof(uint64), 8);
+}
+
+//**********************
+// END testcases here
+//**********************
+
+CU_pSuite test_etch_suite()
+{
+ CU_pSuite ps = CU_add_suite("etch_suite", init_suite, clean_suite);
+ CU_add_test(ps, "test etch_datatypes", test_etch_datatypes);
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_etch_config.c b/binding-c/runtime/c/src/test/common/test_etch_config.c
new file mode 100644
index 0000000..5d1470f
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_etch_config.c
@@ -0,0 +1,247 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_etch_config.c -- test etch config
+ */
+#include <stdio.h>
+#include <wchar.h>
+#include "CUnit.h"
+
+#include "etch_runtime.h"
+#include "etch_config.h"
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ return 0;
+}
+
+//**********************
+// START testcases here
+//**********************
+
+void test_etch_config_create_destroy(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ etch_config_t* config = NULL;
+
+ etch_status = etch_config_create(&config);
+ CU_ASSERT_PTR_NOT_NULL(config);
+
+ etch_config_destroy(config);
+}
+/*
+static void test_etch_config_open(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ etch_config_t* config = NULL;
+
+ etch_status = etch_config_create(&config);
+ CU_ASSERT_PTR_NOT_NULL(config);
+
+ etch_status = etch_config_open(config, "C:\\etch_config.properties");
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_config_destroy(config);
+}
+*/
+static void test_etch_config_set_property(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ etch_config_t* config = NULL;
+ char* value = NULL;
+
+ etch_status = etch_config_create(&config);
+ CU_ASSERT_PTR_NOT_NULL(config);
+
+ etch_status = etch_config_clear(config);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_config_set_property(NULL, NULL, NULL);
+ CU_ASSERT_EQUAL(etch_status, ETCH_EINVAL);
+
+ etch_status = etch_config_set_property(config, NULL, NULL);
+ CU_ASSERT_EQUAL(etch_status, ETCH_EINVAL);
+
+ etch_status = etch_config_set_property(config, "test.property", NULL);
+ CU_ASSERT_EQUAL(etch_status, ETCH_EINVAL);
+
+ etch_status = etch_config_set_property(config, "test.property", "value");
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_config_set_property(config, "test.property", "new value");
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_config_get_property_string(config, "test.property", &value);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+ CU_ASSERT_EQUAL(strcmp(value, "new value"), 0);
+
+ // check resize
+ etch_status = etch_config_set_property(config, "test.property1", "value");
+ etch_status = etch_config_set_property(config, "test.property2", "value");
+ etch_status = etch_config_set_property(config, "test.property3", "value");
+ etch_status = etch_config_set_property(config, "test.property4", "value");
+ etch_status = etch_config_set_property(config, "test.property5", "value");
+ etch_status = etch_config_set_property(config, "test.property6", "value");
+ etch_status = etch_config_set_property(config, "test.property7", "value");
+ etch_status = etch_config_set_property(config, "test.property8", "value");
+ etch_status = etch_config_set_property(config, "test.property9", "value");
+ etch_status = etch_config_set_property(config, "test.property10", "value");
+ etch_status = etch_config_set_property(config, "test.property11", "value");
+ CU_ASSERT_EQUAL(etch_config_get_length(config), 12);
+ CU_ASSERT_EQUAL(etch_config_get_size(config), 20);
+
+ etch_config_destroy(config);
+}
+
+static void test_etch_config_get_property_string(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ etch_config_t* config = NULL;
+ char* value1 = NULL;
+
+ etch_status = etch_config_create(&config);
+ CU_ASSERT_PTR_NOT_NULL(config);
+
+ etch_status = etch_config_set_property(config, "test.property", "value");
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_config_get_property_string(config, NULL, &value1);
+ CU_ASSERT_EQUAL(etch_status, ETCH_EINVAL);
+
+ etch_status = etch_config_get_property_string(config, NULL, NULL);
+ CU_ASSERT_EQUAL(etch_status, ETCH_EINVAL);
+
+ etch_status = etch_config_get_property_string(config, "test.property", &value1);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+ CU_ASSERT_EQUAL(strcmp(value1, "value"), 0);
+
+ etch_status = etch_config_get_property_string(config, "test.property1", &value1);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+ CU_ASSERT_PTR_NULL(value1);
+
+ etch_config_destroy(config);
+}
+
+static void test_etch_config_get_property_int(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ etch_config_t* config = NULL;
+ int32 value1 = 0;
+
+ etch_status = etch_config_create(&config);
+ CU_ASSERT_PTR_NOT_NULL(config);
+
+ etch_status = etch_config_set_property(config, "test.property", "10");
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_config_set_property(config, "test.property1", "fdg");
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_config_get_property_int(config, NULL, &value1);
+ CU_ASSERT_EQUAL(etch_status, ETCH_EINVAL);
+
+ etch_status = etch_config_get_property_int(config, NULL, NULL);
+ CU_ASSERT_EQUAL(etch_status, ETCH_EINVAL);
+
+ etch_status = etch_config_get_property_int(config, "test.property", &value1);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+ CU_ASSERT_EQUAL(value1, 10);
+
+ etch_status = etch_config_get_property_int(config, "test.property1", &value1);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_config_get_property_int(config, "test.property1", &value1);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_config_destroy(config);
+}
+
+static void test_etch_config_get_property_by_index(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ etch_config_t* config = NULL;
+ char* value1 = NULL;
+
+ etch_status = etch_config_create(&config);
+ CU_ASSERT_PTR_NOT_NULL(config);
+
+ etch_status = etch_config_clear(config);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_config_set_property(config, "test.property", "value");
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_config_get_property_by_index(config, 0, &value1);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+ CU_ASSERT_PTR_NOT_NULL(value1);
+ if(value1 != NULL) {
+ CU_ASSERT_EQUAL(strcmp("value", value1), 0);
+ }
+
+ etch_config_destroy(config);
+}
+
+static void test_etch_config_has_property(void)
+{
+ int result = 0;
+
+ etch_status_t etch_status = ETCH_SUCCESS;
+ etch_config_t* config = NULL;
+
+ etch_status = etch_config_create(&config);
+ CU_ASSERT_PTR_NOT_NULL(config);
+
+ etch_status = etch_config_set_property(config, "test.property", "value");
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ result = etch_config_has_property(config, "test.property");
+ CU_ASSERT_EQUAL(result, 1);
+
+ result = etch_config_has_property(config, "test.property2");
+ CU_ASSERT_EQUAL(result, 0);
+
+ etch_config_destroy(config);
+}
+
+//**********************
+// END testcases here
+//**********************
+
+CU_pSuite test_etch_config_suite()
+{
+ CU_pSuite ps = CU_add_suite("etch_config", init_suite, clean_suite);
+ CU_add_test(ps, "test etch_config_create_destroy", test_etch_config_create_destroy);
+ //CU_add_test(ps, "test etch_config_open", test_etch_config_open);
+ CU_add_test(ps, "test etch_config_set_property", test_etch_config_set_property);
+ CU_add_test(ps, "test etch_config_get_property_string", test_etch_config_get_property_string);
+ CU_add_test(ps, "test etch_config_get_property_int", test_etch_config_get_property_int);
+ CU_add_test(ps, "test etch_config_get_property_by_index", test_etch_config_get_property_by_index);
+ CU_add_test(ps, "test etch_config_has_property", test_etch_config_has_property);
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_etch_encoding.c b/binding-c/runtime/c/src/test/common/test_etch_encoding.c
new file mode 100644
index 0000000..ee44229
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_etch_encoding.c
@@ -0,0 +1,231 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_encoding.c -- test etch encoding
+ */
+#include "etch_runtime.h"
+#include "etch_encoding.h"
+#include "etch_mem.h"
+
+#include "CUnit.h"
+#include <stdio.h>
+#include <wchar.h>
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+/*
+static void test_etch_encoding_unicode_to_utf8(void)
+{
+ int result = 0;
+ char* out = NULL;
+
+ result = etch_encoding_unicode_to_utf8(&out, L"U\u00A9");
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL(out);
+ if(out != NULL)
+ {
+ CU_ASSERT_EQUAL(out[0], 'U');
+ CU_ASSERT_EQUAL(out[1], (char)0xc2);
+ CU_ASSERT_EQUAL(out[2], (char)0xa9);
+ }
+ if(out != NULL)
+ {
+ etch_free(out);
+ out = NULL;
+ }
+}
+
+static void test_etch_encoding_unicode_to_ansi(void)
+{
+ int result = 0;
+ char* out = NULL;
+
+ result = etch_encoding_unicode_to_ansi(&out, L"UNICODE String");
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL(out);
+ CU_ASSERT_STRING_EQUAL(out, "UNICODE String");
+ if(out != NULL)
+ {
+ etch_free(out);
+ out = NULL;
+ }
+}
+
+static void test_etch_encoding_utf8_to_unicode(void)
+{
+ int result = 0;
+ char in[] = {'U', 0xc2, 0xa9, 0x00};
+ wchar_t* out = NULL;
+
+ result = etch_encoding_utf8_to_unicode(&out, in);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL(out);
+ if(out != NULL)
+ {
+ CU_ASSERT_EQUAL(out[0], L'U');
+ CU_ASSERT_EQUAL(out[1], L'\u00A9');
+ }
+ if(out != NULL)
+ {
+ etch_free(out);
+ out = NULL;
+ }
+ if(out != NULL)
+ {
+ etch_free(out);
+ out = NULL;
+ }
+}
+
+static void test_etch_encoding_ansi_to_unicode(void)
+{
+ int result = 0;
+ wchar_t* out = NULL;
+
+ result = etch_encoding_ansi_to_unicode(&out, "UNICODE");
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL(out);
+ CU_ASSERT_EQUAL(wcscmp(out, L"UNICODE"), 0);
+ if(out != NULL)
+ {
+ etch_free(out);
+ out = NULL;
+ }
+ if(out != NULL)
+ {
+ etch_free(out);
+ out = NULL;
+ }
+}
+
+static void test_etch_encoding_get_unicode_bytecount(void)
+{
+ size_t count1 = 0;
+ size_t count2 = 0;
+ wchar_t* str = L"UNICODE";
+ count1 = (wcslen(str) + 1) * sizeof(wchar_t);
+
+ count2 = etch_encoding_get_unicode_bytecount(str);
+ CU_ASSERT_EQUAL(count1, count2);
+
+}
+*/
+static void test_etch_encoding_transcode_ucs2_utf8(void)
+{
+
+ char* out = 0;
+ unsigned char outEncoding = ETCH_ENCODING_UTF8;
+ const char in[] = {'a', 0,
+ 'b', 0,
+ 'c', 0,
+ 0, 0};
+ unsigned char inEncoding = ETCH_ENCODING_UCS2;
+ unsigned int inByteCount = 4*2;
+ int outBytes;
+ // TODO: pool
+ CU_ASSERT_EQUAL(0, etch_encoding_transcode(&out, outEncoding, in, inEncoding, inByteCount, &outBytes, NULL));
+ CU_ASSERT(out != NULL);
+ if(out){
+ CU_ASSERT_EQUAL('a', out[0]);
+ CU_ASSERT_EQUAL('b', out[1]);
+ CU_ASSERT_EQUAL('c', out[2]);
+ CU_ASSERT_EQUAL(0, out[3]);
+ CU_ASSERT_EQUAL(4, outBytes);
+ etch_free(out);
+ }
+}
+
+static void test_etch_encoding_transcode_ucs4_utf8(void)
+{
+
+ char* out = 0;
+ unsigned char outEncoding = ETCH_ENCODING_UTF8;
+ const char in[] = {'a', 0, 0, 0,
+ 'b', 0, 0, 0,
+ 'c', 0, 0, 0,
+ 0, 0, 0, 0};
+ unsigned char inEncoding = ETCH_ENCODING_UCS4;
+ unsigned int inByteCount = 4*4;
+ int outBytes;
+ // TODO: pool
+ CU_ASSERT_EQUAL(0, etch_encoding_transcode(&out, outEncoding, in, inEncoding, inByteCount, &outBytes, NULL));
+ CU_ASSERT(out != NULL);
+ if(out){
+ CU_ASSERT_EQUAL('a', out[0]);
+ CU_ASSERT_EQUAL('b', out[1]);
+ CU_ASSERT_EQUAL('c', out[2]);
+ CU_ASSERT_EQUAL(0, out[3]);
+ CU_ASSERT_EQUAL(4, outBytes);
+ etch_free(out);
+ }
+}
+
+
+static void test_etch_encoding_transcode_wchar_utf8(void) {
+ char* out = 0;
+ unsigned char outEncoding = ETCH_ENCODING_UTF8;
+ wchar_t *in = L"abc";
+ // TODO: pool
+ CU_ASSERT_EQUAL(0, etch_encoding_transcode_wchar(&out, outEncoding, in, NULL));
+ CU_ASSERT(out != NULL);
+ if(out){
+ CU_ASSERT_EQUAL('a', out[0]);
+ CU_ASSERT_EQUAL('b', out[1]);
+ CU_ASSERT_EQUAL('c', out[2]);
+ CU_ASSERT_EQUAL(0, out[3]);
+ etch_free(out);
+ }
+}
+
+CU_pSuite test_etch_encoding_suite(void)
+{
+ CU_pSuite ps = CU_add_suite("etch_encoding", init_suite, clean_suite);
+ /*
+ CU_add_test(ps, "test etch_encoding_unicode_to_utf8", test_etch_encoding_unicode_to_utf8);
+ CU_add_test(ps, "test etch_encoding_unicode_to_ansi", test_etch_encoding_unicode_to_ansi);
+ CU_add_test(ps, "test etch_encoding_utf8_to_unicode", test_etch_encoding_utf8_to_unicode);
+ CU_add_test(ps, "test etch_encoding_ansi_to_unicode", test_etch_encoding_ansi_to_unicode);
+ CU_add_test(ps, "test etch_encoding_get_unicode_bytecount", test_etch_encoding_get_unicode_bytecount);
+ */
+ CU_add_test(ps, "test etch_encoding_transcode_ucs2_utf8", test_etch_encoding_transcode_ucs2_utf8);
+ CU_add_test(ps, "test etch_encoding_transcode_ucs4_utf8", test_etch_encoding_transcode_ucs4_utf8);
+ CU_add_test(ps, "test etch_encoding_transcode_wchar_utf8", test_etch_encoding_transcode_wchar_utf8);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_etch_linked_list.c b/binding-c/runtime/c/src/test/common/test_etch_linked_list.c
new file mode 100644
index 0000000..bda8b92
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_etch_linked_list.c
@@ -0,0 +1,392 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_runtime.c -- test etch runtime
+ */
+#include "etch_runtime.h"
+#include "etch_linked_list.h"
+
+#include <stdio.h>
+#include <wchar.h>
+#include "CUnit.h"
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ return 0;
+}
+
+//**********************
+// START testcases here
+//**********************
+
+static void test_etch_linked_list_create_destroy(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_linked_list_t* list = NULL;
+
+ status = etch_linked_list_create(&list, 0);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+}
+
+static void test_etch_linked_list_add(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_linked_list_t* list = NULL;
+ char* data = "test_0";
+ uint32 count = 0;
+ uint32 i = 0;
+
+ status = etch_linked_list_add(NULL, NULL);
+ CU_ASSERT_EQUAL(status, ETCH_EINVAL);
+
+ status = etch_linked_list_create(&list, 0);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_add(list, data);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 1);
+
+ status = etch_linked_list_remove(list, data);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 0);
+
+ status = etch_linked_list_add(list, NULL);
+ CU_ASSERT_EQUAL(status, ETCH_EINVAL);
+
+ status = etch_linked_list_add(list, "test_1");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_add(list, "test_2");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_create(&list, ETCH_LINKED_LIST_DATA_FREE);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_add(list, etch_malloc(10, 0));
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_add(list, etch_malloc(20, 0));
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_create(&list, 0);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ for(i = 0; i < 1000; i++) {
+ status = etch_linked_list_add(list, data);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ }
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 1000);
+
+ for(i = 0; i < 1000; i++) {
+ status = etch_linked_list_remove(list, data);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ }
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 0);
+
+
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+}
+
+void test_etch_linked_list_count(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_linked_list_t* list = NULL;
+ uint32 count = 0;
+
+ status = etch_linked_list_create(&list, 0);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 0);
+
+ status = etch_linked_list_add(list, "test_1");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 1);
+
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+}
+
+void test_etch_linked_list_contains(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_linked_list_t* list = NULL;
+ uint32 contains = 0;
+ char* data = "test_1";
+
+ status = etch_linked_list_create(&list, 0);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_add(list, "test_2");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_add(list, data);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ contains = etch_linked_list_contains(NULL, NULL);
+ CU_ASSERT_EQUAL(contains, 0);
+
+ contains = etch_linked_list_contains(list, NULL);
+ CU_ASSERT_EQUAL(contains, 0);
+
+ contains = etch_linked_list_contains(list, "test_3");
+ CU_ASSERT_EQUAL(contains, 0);
+
+ contains = etch_linked_list_contains(list, data);
+ CU_ASSERT_EQUAL(contains, 1);
+
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+}
+
+void test_etch_linked_list_insert(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_linked_list_t* list = NULL;
+ uint32 count = 0;
+
+ status = etch_linked_list_create(&list, 0);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_insert(list, 2, "test_1");
+ CU_ASSERT_EQUAL(status, ETCH_EOUTOFBOUNDS);
+
+ status = etch_linked_list_add(list, "test_2");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 1);
+
+ status = etch_linked_list_insert(list, 0, "test_3");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 2);
+
+ status = etch_linked_list_insert(list, 1, "test_4");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 3);
+
+ status = etch_linked_list_insert(list, 2, "test_4");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 4);
+
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+}
+
+void test_etch_linked_list_get(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_linked_list_t* list = NULL;
+ uint32 count = 0;
+ uint32 i = 0;
+ char* data = NULL;
+
+ status = etch_linked_list_create(&list, 0);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_add(list, "test_0 added");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_insert(list, 0, "test_1 insert 0");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_insert(list, 1, "test_2 insert 1");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_insert(list, 2, "test_3 insert 2");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 4);
+
+ for(i = 0; i < count; i++) {
+ status = etch_linked_list_get(list, i, (void**)&data);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ //printf("%s\n", data);
+ }
+
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+}
+
+void test_etch_linked_list_remove_at(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_linked_list_t* list = NULL;
+ uint32 count = 0;
+
+ status = etch_linked_list_create(&list, 0);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_add(list, "test_0 added");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_remove_at(list, 0);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 0);
+
+ status = etch_linked_list_add(list, "test_0 added");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ status = etch_linked_list_add(list, "test_1 added");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ status = etch_linked_list_add(list, "test_2 added");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 3);
+ status = etch_linked_list_remove_at(list, 1);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 2);
+
+ status = etch_linked_list_remove_at(list, 1);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 1);
+
+ status = etch_linked_list_remove_at(list, 0);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 0);
+
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ // check remove of last list element
+ status = etch_linked_list_create(&list, 0);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_add(list, "test_0 added");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ status = etch_linked_list_add(list, "test_1 added");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ status = etch_linked_list_add(list, "test_2 added");
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_linked_list_remove_at(list, 2);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 2);
+
+ status = etch_linked_list_remove_at(list, 1);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ status = etch_linked_list_remove_at(list, 0);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 0);
+
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+}
+
+void test_etch_linked_list_remove(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_linked_list_t* list = NULL;
+ uint32 count = 0;
+ char* data = NULL;
+
+ status = etch_linked_list_create(&list, 0);
+ CU_ASSERT_PTR_NOT_NULL(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ data = "test_0 added";
+
+ status = etch_linked_list_add(list, data);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 1);
+
+ status = etch_linked_list_remove(list, data);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ count = etch_linked_list_count(list);
+ CU_ASSERT_EQUAL(count, 0);
+
+ status = etch_linked_list_destroy(list);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+}
+
+//**********************
+// END testcases here
+//**********************
+
+/**
+ * main
+ */
+CU_pSuite test_etch_linked_list_suite()
+{
+ CU_pSuite ps = CU_add_suite("etch_runtime", init_suite, clean_suite);
+
+ CU_add_test(ps, "test_etch_linked_list_create_destroy", test_etch_linked_list_create_destroy);
+ CU_add_test(ps, "test_etch_linked_list_add", test_etch_linked_list_add);
+ CU_add_test(ps, "test_etch_linked_list_count", test_etch_linked_list_count);
+ CU_add_test(ps, "test_etch_linked_list_contains", test_etch_linked_list_contains);
+ CU_add_test(ps, "test_etch_linked_list_insert", test_etch_linked_list_insert);
+ CU_add_test(ps, "test_etch_linked_list_get", test_etch_linked_list_get);
+ CU_add_test(ps, "test_etch_linked_list_remove_at", test_etch_linked_list_remove_at);
+ CU_add_test(ps, "test_etch_linked_list_remove", test_etch_linked_list_remove);
+
+ return ps;
+
+
+
+
+}
diff --git a/binding-c/runtime/c/src/test/common/test_etch_log.c b/binding-c/runtime/c/src/test/common/test_etch_log.c
new file mode 100644
index 0000000..b97abfe
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_etch_log.c
@@ -0,0 +1,146 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_runtime.c -- test etch runtime
+ */
+#include "etch_runtime.h"
+#include "etch_config.h"
+#include "etch_log.h"
+
+#include <stdio.h>
+#include <wchar.h>
+#include "CUnit.h"
+
+static const char* TEST_ETCH_LOG_CATEGORY = "test_etch_config";
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ return 0;
+}
+
+static etch_status_t etch_log_appender_test_create(void** appender, etch_config_t* config)
+{
+ *appender = NULL;
+ return ETCH_SUCCESS;
+}
+
+static etch_status_t etch_log_appender_test_open(void* appender)
+{
+ //printf("etch_log_appender_test_open\n");
+ return ETCH_SUCCESS;
+}
+
+static etch_status_t etch_log_appender_test_log(void* appender, etch_log_message_t* message)
+{
+ //printf("etch_log_appender_test_log\n");
+ return ETCH_SUCCESS;
+}
+
+static etch_status_t etch_log_appender_test_close(void* appender)
+{
+ //printf("etch_log_appender_test_close\n");
+ return ETCH_SUCCESS;
+}
+
+static etch_status_t etch_log_appender_test_destroy(void* appender)
+{
+ return ETCH_SUCCESS;
+}
+
+static struct etch_log_appender_desc etch_log_appender_test_desc = {
+ etch_log_appender_test_create,
+ etch_log_appender_test_open,
+ etch_log_appender_test_log,
+ etch_log_appender_test_close,
+ etch_log_appender_test_destroy
+};
+
+
+//**********************
+// START testcases here
+//**********************
+
+static void test_etch_log_register_appender(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_log_register_appender("testappender", &etch_log_appender_test_desc);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+}
+
+static void test_etch_log_create_destroy(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ etch_config_t* config = NULL;
+ etch_log_t* logger = NULL;
+
+ etch_status = etch_config_create(&config);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_log_create(&logger, config);
+ CU_ASSERT_PTR_NOT_NULL(logger);
+
+ etch_log_destroy(logger);
+ etch_config_destroy(config);
+}
+
+static void test_etch_log_log(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ etch_config_t* config = NULL;
+
+ etch_status = etch_config_create(&config);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_runtime_initialize(config);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ ETCH_LOG(TEST_ETCH_LOG_CATEGORY, ETCH_LOG_ERROR, "test log message");
+ ETCH_LOG(TEST_ETCH_LOG_CATEGORY, ETCH_LOG_XDEBUG, "test log message %s", "test1");
+ ETCH_LOG(TEST_ETCH_LOG_CATEGORY, ETCH_LOG_INFO, "test log message %s", "test2");
+
+ etch_runtime_shutdown();
+ etch_config_destroy(config);
+}
+
+//**********************
+// END testcases here
+//**********************
+
+CU_pSuite test_etch_log_suite()
+{
+ CU_pSuite ps = CU_add_suite("etch_log test suite", init_suite, clean_suite);
+
+ CU_add_test(ps, "test etch_log_register_appender", test_etch_log_register_appender);
+ CU_add_test(ps, "test etch_log_create_destroy", test_etch_log_create_destroy);
+ CU_add_test(ps, "test etch_log_log", test_etch_log_log);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_etch_mutex.c b/binding-c/runtime/c/src/test/common/test_etch_mutex.c
new file mode 100644
index 0000000..fb049b8
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_etch_mutex.c
@@ -0,0 +1,151 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_mutex.c -- test etch mutex
+ */
+#include "etch_runtime.h"
+#include "etch_mutex.h"
+
+#include <stdio.h>
+#include <wchar.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+//**********************
+// START testcases here
+//**********************
+
+static void test_apr_mutex(void)
+{
+ apr_status_t apr_status = APR_SUCCESS;
+ apr_thread_mutex_t* apr_mutex = NULL;
+
+ // test create, lock, unlock, destory
+ // create mutex
+ apr_status = apr_thread_mutex_create(&apr_mutex, APR_THREAD_MUTEX_NESTED, g_etch_main_pool);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+ // lock mutex
+ apr_status = apr_thread_mutex_lock(apr_mutex);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+ // unlock mutex
+ apr_status = apr_thread_mutex_unlock(apr_mutex);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+ // destroy mutex
+ apr_status = apr_thread_mutex_destroy(apr_mutex);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+ apr_mutex = NULL;
+
+
+ // test create, lock, lock, destory
+ // create mutex
+ apr_status = apr_thread_mutex_create(&apr_mutex, APR_THREAD_MUTEX_NESTED, g_etch_main_pool);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+ // lock mutex first time
+ apr_status = apr_thread_mutex_lock(apr_mutex);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+ // lock mutex second time
+ apr_status = apr_thread_mutex_lock(apr_mutex);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+ // unlock mutex second time
+ apr_status = apr_thread_mutex_unlock(apr_mutex);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+ apr_status = apr_thread_mutex_unlock(apr_mutex);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+ // destroy mutex
+ apr_status = apr_thread_mutex_destroy(apr_mutex);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+ if(apr_status != APR_SUCCESS) {
+ char buffer[1024];
+ apr_strerror(apr_status, buffer, 1024);
+ printf("error: %s\n", buffer);
+ }
+ apr_mutex = NULL;
+}
+
+static void test_etch_mutex_create_destory(void)
+{
+ etch_status_t status = APR_SUCCESS;
+ etch_mutex_t* mutex = NULL;
+
+ status = etch_mutex_create(&mutex, ETCH_MUTEX_UNNESTED, g_etch_main_pool);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_mutex_lock(mutex);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_mutex_trylock(mutex);
+ CU_ASSERT_EQUAL(status, ETCH_EBUSY);
+
+ status = etch_mutex_unlock(mutex);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_mutex_unlock(mutex);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_mutex_unlock(mutex);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_mutex_destroy(mutex);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+}
+
+//**********************
+// END testcases here
+//**********************
+
+/**
+ * main
+ */
+CU_pSuite test_etch_mutex_suite()
+{
+ CU_pSuite ps = CU_add_suite("etch_mutex", init_suite, clean_suite);
+
+ CU_add_test(ps, "test apr_mutex", test_apr_mutex);
+ CU_add_test(ps, "test etch_mutex_create_destory", test_etch_mutex_create_destory);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_etch_runtime.c b/binding-c/runtime/c/src/test/common/test_etch_runtime.c
new file mode 100644
index 0000000..4cf2d32
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_etch_runtime.c
@@ -0,0 +1,198 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_runtime.c -- test etch runtime
+ */
+#include "etch_runtime.h"
+#include "etch_config.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include <wchar.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+static int init_suite(void)
+{
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ return 0;
+}
+
+//**********************
+// START testcases here
+//**********************
+
+static void test_etch_runtime_get_version(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ uint16 major = 0;
+ uint16 minor = 0;
+ uint16 revision = 0;
+
+ // initialize runtime
+ etch_status = etch_runtime_get_version(NULL, NULL, NULL);
+ CU_ASSERT_EQUAL(etch_status, ETCH_EINVAL);
+
+ etch_status = etch_runtime_get_version(&major, &minor, &revision);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+}
+
+
+static void test_etch_runtime_initialize_shutdown(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ // initialize runtime
+ etch_status = etch_runtime_initialize(NULL);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+ // check apr pool
+ CU_ASSERT_PTR_NOT_NULL(g_etch_main_pool);
+ // shutdown runtime
+ etch_runtime_shutdown();
+ // check apr pool
+ CU_ASSERT_PTR_NULL(g_etch_main_pool);
+}
+
+static void test_etch_runtime_get_config(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ etch_config_t *config = NULL;
+ etch_config_t *configtemp = NULL;
+
+ etch_status = etch_config_create(&config);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_runtime_initialize(config);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+
+ etch_status = etch_runtime_get_config(&configtemp);
+ CU_ASSERT_EQUAL(etch_status, ETCH_SUCCESS);
+ CU_ASSERT_PTR_NOT_NULL(configtemp);
+
+ // shutdown runtime
+ etch_runtime_shutdown();
+
+ etch_config_destroy(config);
+}
+
+static void test_etch_runtime_multiple_times(void)
+{
+ etch_status_t etch_status_runtime = ETCH_SUCCESS;
+ etch_status_t etch_status_shutdown = ETCH_SUCCESS;
+ int i=0;
+
+ for (i=0; i<5; i++)
+ {
+ etch_status_runtime = etch_runtime_initialize(NULL);
+ CU_ASSERT_EQUAL(etch_status_runtime, ETCH_SUCCESS);
+
+ // shutdown runtime
+ etch_status_shutdown = etch_runtime_shutdown();
+ CU_ASSERT_EQUAL(etch_status_shutdown, ETCH_SUCCESS);
+ }
+}
+
+
+static void test_etch_runtime_config_multiple_times(void)
+{
+ etch_status_t etch_status_config = ETCH_SUCCESS;
+ etch_status_t etch_status_runtime = ETCH_SUCCESS;
+ etch_status_t etch_status_shutdown = ETCH_SUCCESS;
+ etch_config_t *config;
+ etch_config_t *configtemp = NULL;
+
+ int i=0;
+
+ for (i=0; i<5; i++)
+ {
+ etch_status_config = etch_config_create(&config);
+ CU_ASSERT_EQUAL(etch_status_config, ETCH_SUCCESS);
+
+ etch_status_runtime = etch_runtime_initialize(config);
+ CU_ASSERT_EQUAL(etch_status_runtime, ETCH_SUCCESS);
+
+ etch_status_config = etch_runtime_get_config(&configtemp);
+ CU_ASSERT_EQUAL(etch_status_config, ETCH_SUCCESS);
+ CU_ASSERT_PTR_NOT_NULL(configtemp);
+
+ // shutdown runtime
+ etch_status_shutdown = etch_runtime_shutdown();
+ CU_ASSERT_EQUAL(etch_status_shutdown, ETCH_SUCCESS);
+
+ etch_status_config = etch_config_destroy(config);
+ CU_ASSERT_EQUAL(etch_status_config, ETCH_SUCCESS);
+ }
+}
+
+static void test_etch_runtime_initialize_shutdown_refcount(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+
+ /* 1 initialisation */
+ status = etch_runtime_initialize(NULL);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ /* 2 initialisation */
+ status = etch_runtime_initialize(NULL);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ /* 3 initialisation */
+ status = etch_runtime_initialize(NULL);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ // check apr pool
+ CU_ASSERT_PTR_NOT_NULL(g_etch_main_pool);
+
+ /* 1 shutdown */
+ status = etch_runtime_shutdown();
+ /* 2 shutdown */
+ status = etch_runtime_shutdown();
+ /* 3 shutdown */
+ status = etch_runtime_shutdown();
+
+ // check apr pool
+ CU_ASSERT_PTR_NULL(g_etch_main_pool);
+}
+
+
+//**********************
+// END testcases here
+//**********************
+
+/**
+ * main
+ */
+CU_pSuite test_etch_runtime_suite(int argc, char** argv)
+{
+ CU_pSuite ps = CU_add_suite("etch_runtime", init_suite, clean_suite);
+
+ CU_add_test(ps, "test_etch_runtime_get_version", test_etch_runtime_get_version);
+ CU_add_test(ps, "test_etch_runtime_initialize_shutdown", test_etch_runtime_initialize_shutdown);
+ CU_add_test(ps, "test_etch_runtime_get_config", test_etch_runtime_get_config);
+ CU_add_test(ps, "test_etch_runtime_multiple_times", test_etch_runtime_multiple_times);
+ CU_add_test(ps, "test_etch_runtime_config_multiple_times", test_etch_runtime_config_multiple_times);
+ CU_add_test(ps, "test_etch_runtime_initialize_shutdown_refcount", test_etch_runtime_initialize_shutdown_refcount);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_etch_thread.c b/binding-c/runtime/c/src/test/common/test_etch_thread.c
new file mode 100644
index 0000000..28ab28a
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_etch_thread.c
@@ -0,0 +1,85 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_etch_threadpool.c -- test etch threadpool
+ */
+#include "etch_runtime.h"
+#include "etch_thread2.h"
+#include "etch_mem.h"
+
+#include "CUnit.h"
+#include <stdio.h>
+#include <wchar.h>
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+//**********************
+// START testcases here
+//**********************
+
+static void test_etch_thread_create_destroy(void)
+{
+ //status = etch_thread_create(&thread,
+
+
+
+ //status = etch_threadpool_create(&threadpool, ETCH_THREADPOOL_TYPE_FREE, 5);
+ //CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ //status = etch_threadpool_destroy(threadpool);
+ //CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+}
+
+//**********************
+// END testcases here
+//**********************
+
+/**
+ * main
+ */
+CU_pSuite test_etch_thread_suite()
+{
+ CU_pSuite ps = CU_add_suite("etch_thread", init_suite, clean_suite);
+
+ CU_add_test(ps, "test etch_thread_create_destroy", test_etch_thread_create_destroy);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_etch_wait.c b/binding-c/runtime/c/src/test/common/test_etch_wait.c
new file mode 100644
index 0000000..fd22cc8
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_etch_wait.c
@@ -0,0 +1,167 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_etch_wait.c -- test etch wait
+ */
+#include "etch_runtime.h"
+#include "etch_wait.h"
+
+#include "apr_thread_proc.h"
+
+#include <stdio.h>
+#include <wchar.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+//**********************
+// START testcases here
+//**********************
+
+typedef struct apr_thread_data_t
+{
+ etch_wait_t* waiter;
+ uint16 mode;
+ uint16 rv;
+} apr_thread_data_t;
+
+static void* APR_THREAD_FUNC thread_func1(apr_thread_t *thd, void *data)
+{
+ apr_status_t status;
+ apr_thread_data_t* thd_data = data;
+ clock_t start = 0;
+ clock_t end = 0;
+ clock_t diff = 0;
+
+ switch(thd_data->mode) {
+ case 0:
+ start = clock();
+ status = etch_wait_timedwait(thd_data->waiter, 4, 400);
+ CU_ASSERT_EQUAL(status, ETCH_ETIMEOUT);
+ end = clock();
+ diff = end - start;
+ break;
+ case 1:
+ start = clock();
+ status = etch_wait_timedwait(thd_data->waiter, 2, 500);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+ end = clock();
+ diff = end - start;
+ break;
+ }
+
+ apr_thread_exit(thd, 0);
+ return NULL;
+}
+
+static void test_etch_wait_create_destroy(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_wait_t* waiter = NULL;
+
+ status = etch_wait_create(&waiter, g_etch_main_pool);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_wait_destroy(waiter);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+}
+
+static void test_etch_wait_timedwait(void)
+{
+ etch_status_t status = ETCH_SUCCESS;
+ etch_wait_t* waiter = NULL;
+
+
+ apr_status_t apr_status;
+ apr_status_t apr_thread_status;
+ apr_thread_data_t apr_thread_data;
+ apr_thread_t* apr_thread = NULL;
+
+ status = etch_wait_create(&waiter, g_etch_main_pool);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_wait_timedwait(waiter, 2, 100);
+ CU_ASSERT_EQUAL(status, ETCH_ETIMEOUT);
+
+ // timeout / signal test
+ apr_thread_data.waiter = waiter;
+ apr_thread_data.mode = 0;
+
+ apr_status = apr_thread_create(&apr_thread, NULL, thread_func1, &apr_thread_data, g_etch_main_pool);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+
+ apr_sleep(200);
+ etch_wait_set(waiter, 1);
+
+ apr_status = apr_thread_join(&apr_thread_status, apr_thread);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+
+ // timeout / signal test
+ apr_thread_data.waiter = waiter;
+ apr_thread_data.mode = 1;
+
+ apr_status = apr_thread_create(&apr_thread, NULL, thread_func1, &apr_thread_data, g_etch_main_pool);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+
+ etch_wait_set(waiter, 2);
+
+ apr_status = apr_thread_join(&apr_thread_status, apr_thread);
+ CU_ASSERT_EQUAL(apr_status, APR_SUCCESS);
+
+ status = etch_wait_destroy(waiter);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+}
+
+//**********************
+// END testcases here
+//**********************
+
+/**
+ * main
+ */
+CU_pSuite test_etch_wait_suite()
+{
+ CU_pSuite ps = CU_add_suite("etch_wait", init_suite, clean_suite);
+
+ CU_add_test(ps, "test test_etch_wait_create_destroy", test_etch_wait_create_destroy);
+ CU_add_test(ps, "test test_etch_wait_timedwait", test_etch_wait_timedwait);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_etchobject.c b/binding-c/runtime/c/src/test/common/test_etchobject.c
new file mode 100644
index 0000000..d09231e
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_etchobject.c
@@ -0,0 +1,1056 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_etchobject.c.c -- test etch object inheritance and primitives
+ */
+#include "etch_runtime.h"
+#include "etch_object.h"
+#include "etch_objecttypes.h"
+#include "etch_nativearray.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+#include <wchar.h>
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/**
+ * class_a: base class
+ */
+typedef struct class_a
+{
+ etch_object object;
+
+ etch_string* a_string;
+
+} class_a;
+
+
+/**
+ * class_a destructor
+ */
+static int destroy_class_a(void* data)
+{
+ class_a* thisp = (class_a*)data;
+ if (thisp->a_string)
+ etch_object_destroy(thisp->a_string);
+
+ destroy_object((etch_object*) thisp);
+ return 0;
+}
+
+/**
+ * class_a copy consttructor
+ */
+static void* clone_class_a(void* data)
+{
+ class_a* origobj = (class_a*)data;
+ class_a* newobj = (class_a*) new_object(sizeof(class_a), 413, 0);
+ memcpy(newobj, origobj, sizeof(etch_object));
+ newobj->a_string = (etch_string*)etch_object_clone_func(origobj->a_string);
+ return newobj;
+}
+
+
+/**
+ * class_a constructor
+ */
+static class_a* new_class_a(const wchar_t* strval)
+{
+ class_a* newobj = (class_a*) new_object(sizeof(class_a), 403, 0);
+ ((etch_object*)newobj)->destroy = destroy_class_a;
+ ((etch_object*)newobj)->clone = clone_class_a;
+ newobj->a_string = new_stringw(strval);
+ return newobj;
+}
+
+
+/**
+ * class_b: inherits from class_a
+ */
+typedef struct class_b
+{
+ etch_object object;
+
+ char* data;
+ int datasize;
+
+} class_b;
+
+
+/**
+ * class_b destructor
+ */
+static int destroy_class_b(void* data)
+{
+ class_b* thisp = (class_b*)data;
+ etch_free(thisp->data);
+ destroy_object((etch_object*) thisp);
+ return 0;
+}
+
+
+/**
+ * class_b copy consttructor
+ */
+static void* clone_class_b(void* data)
+{
+ class_b* origobj = (class_b*)data;
+ class_b* newobj = (class_b*) new_object(sizeof(class_b), 415, 0);
+ memcpy(newobj, origobj, sizeof(etch_object));
+
+ ((etch_object*)newobj)->parent = ((etch_object*)origobj)->parent?
+ etch_object_clone_func(((etch_object*)origobj)->parent):
+ (etch_object*)new_class_a(NULL);
+
+ if (origobj->data)
+ {
+ newobj->data = etch_malloc(origobj->datasize, 412);
+ newobj->datasize = origobj->datasize;
+ memcpy(newobj->data, origobj->data, origobj->datasize);
+ }
+
+ return newobj;
+}
+
+
+/**
+ * class_b constructor
+ */
+static class_b* new_class_b(class_a* parent, const int datalen)
+{
+ class_b* newobj = (class_b*) new_object(sizeof(class_b), 404, 0);
+ ((etch_object*)newobj)->parent = (etch_object*)(parent? parent: new_class_a(NULL));
+ ((etch_object*)newobj)->destroy = destroy_class_b;
+ ((etch_object*)newobj)->clone = clone_class_b;
+ newobj->data = etch_malloc(datalen, 402);
+ memset(newobj->data, 'x', datalen);
+ newobj->datasize = datalen;
+ return newobj;
+}
+
+
+/**
+ * class_c: inherits from class_b
+ */
+typedef struct class_c
+{
+ etch_object object;
+
+ int* intarray;
+ int numitems;
+
+} class_c;
+
+
+/**
+ * class_c destructor
+ */
+static int destroy_class_c(void* data)
+{
+ class_c* thisp = (class_c*)data;
+ etch_free(thisp->intarray);
+ destroy_object((etch_object*) thisp);
+ return 0;
+}
+
+
+/**
+ * class_c copy consttructor
+ */
+static void* clone_class_c(void* data)
+{
+ class_c* origobj = (class_c*)data;
+ class_c* newobj = (class_c*) new_object(sizeof(class_c), 410, 0);
+ memcpy(newobj, origobj, sizeof(etch_object));
+
+ if (((etch_object*)origobj)->parent)
+ ((etch_object*)newobj)->parent = etch_object_clone_func(((etch_object*)origobj)->parent);
+ else ((etch_object*)newobj)->parent = (etch_object*)new_class_b(NULL, 0);
+
+ if (origobj->intarray)
+ {
+ newobj->intarray = etch_malloc(origobj->numitems * sizeof(int), 411);
+ newobj->numitems = origobj->numitems;
+ memcpy(newobj->intarray, origobj->intarray, origobj->numitems * sizeof(int));
+ }
+
+ return newobj;
+}
+
+
+/**
+ * class_c constructor
+ */
+static class_c* new_class_c(class_b* parent)
+{
+ int i = 0;
+ class_c* newobj = (class_c*) new_object(sizeof(class_b), 400, 0);
+ ((etch_object*)newobj)->parent = (etch_object*)(parent? parent: new_class_b(NULL, 0));
+ ((etch_object*)newobj)->destroy = destroy_class_c;
+ ((etch_object*)newobj)->clone = clone_class_c;
+
+ newobj->numitems = 4;
+ newobj->intarray = etch_malloc(4 * sizeof(int), 401);
+ for(; i < 4; i++) newobj->intarray[i] = i;
+
+ return newobj;
+}
+
+
+/**
+ * test_inheritance()
+ * test that in scenario c inherits from b inherits from a,
+ * destroying c destroys b destroys a.
+ */
+static void test_inheritance(void)
+{
+ class_a* class_toplevel = NULL;
+ class_b* class_midlevel = NULL;
+ class_c* class_lowlevel = NULL;
+
+ class_toplevel = new_class_a(L"it works!");
+ class_midlevel = new_class_b(class_toplevel, 128);
+ class_lowlevel = new_class_c(class_midlevel);
+
+ /* any object destructor should recursively destroy() its superclasses */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(((etch_object*)class_lowlevel)->parent);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(((etch_object*)class_midlevel)->parent);
+ CU_ASSERT_PTR_NULL_FATAL(((etch_object*)class_toplevel)->parent);
+
+ etch_object_destroy(class_lowlevel);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_clone()
+ * test validity of cloned objects, also test construction of inherited object
+ * when the parent(s) implementation(s) is/are not specified.
+ * clones must be disposable, i.e. they must own all their memory
+ * including that of their superclasses, such that if c inherits from b
+ * inherits from a, and I clone c giving c', and I then destroy c, then
+ * I can subsequently destroy c' via normal channels.
+ */
+static void test_clone(void)
+{
+ class_c* class_lowlevel = NULL;
+ class_b* class_midlevel = NULL;
+ class_a* class_toplevel = NULL;
+
+ class_c* clone_lowlevel = NULL;
+ class_b* clone_midlevel = NULL;
+ class_a* clone_toplevel = NULL;
+
+ /* construct class c and all its superclasses */
+ class_lowlevel = new_class_c(NULL);
+
+ /* verify that superclasses were created */
+ class_midlevel = (class_b*) ((etch_object*)class_lowlevel)->parent;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(class_midlevel);
+ class_toplevel = (class_a*) ((etch_object*)class_midlevel)->parent;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(class_toplevel);
+ CU_ASSERT_PTR_NULL_FATAL(((etch_object*)class_toplevel)->parent);
+
+ /* clone class c and all its superclasses */
+ clone_lowlevel = (class_c*)etch_object_clone_func(class_lowlevel);
+
+ /* verify that superclasses were created */
+ clone_midlevel = (class_b*) ((etch_object*)clone_lowlevel)->parent;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(clone_midlevel);
+ clone_toplevel = (class_a*) ((etch_object*)clone_midlevel)->parent;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(clone_toplevel);
+ CU_ASSERT_PTR_NULL_FATAL(((etch_object*)clone_toplevel)->parent);
+
+ /* verify that superclass and data clones are not references to old memory */
+ CU_ASSERT_NOT_EQUAL_FATAL(class_midlevel, clone_midlevel);
+ CU_ASSERT_NOT_EQUAL_FATAL(class_toplevel, clone_toplevel);
+
+ if (class_midlevel->data != 0 && clone_midlevel->data != 0)
+ CU_ASSERT_NOT_EQUAL_FATAL(class_midlevel->data, clone_midlevel->data);
+ if (class_toplevel->a_string != 0 && clone_toplevel->a_string != 0)
+ CU_ASSERT_NOT_EQUAL_FATAL(class_toplevel->a_string, clone_toplevel->a_string);
+
+ // clean data of clone_midlevel so we get no memory leaks
+ if(clone_midlevel->data) {
+ etch_free(clone_midlevel->data);
+ }
+
+ /* we don't need to do this for the test, but here we illustrate
+ * the etch C way of accessing superclass data from a subclass,
+ * which is to traverse the parent chain to the class you want,
+ * and cast the parent* to that class, if it is not so cast already.
+ */
+ clone_midlevel->datasize = 1024;
+ clone_midlevel->data = etch_malloc(clone_midlevel->datasize, 419);
+ memset(clone_midlevel->data, '-', clone_midlevel->datasize);
+
+ /* destroy class c with all its superclasses and associated instance data */
+ etch_object_destroy(class_lowlevel);
+
+ /* destroy class c clone and all its superclasses
+ * if the clone of class_c had not properly cloned all its superclasses and
+ * data, this would crash on attempt to free dangling pointer.
+ */
+ etch_object_destroy(clone_lowlevel);
+}
+
+
+static void test_primitive_byte(void)
+{
+ signed char v = 255;
+ etch_byte* newobj = new_byte(v);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newobj);
+ CU_ASSERT_EQUAL(v, newobj->value);
+
+ etch_object_destroy(newobj);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+static void test_primitive_bool(void)
+{
+ etch_boolean* newobj = new_boolean(100);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newobj);
+ CU_ASSERT_EQUAL(newobj->value, TRUE);
+
+ etch_object_destroy(newobj);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+static void test_primitive_int8(void)
+{
+ signed char v = -1;
+ etch_int8* newobj = new_int8(v);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newobj);
+ CU_ASSERT_EQUAL(newobj->value,v);
+
+ etch_object_destroy(newobj);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+static void test_primitive_int16(void)
+{
+ short v = -1;
+ etch_int16* newobj = new_int16(v);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newobj);
+ CU_ASSERT_EQUAL(newobj->value,v);
+
+ etch_object_destroy(newobj);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+static void test_primitive_int32(void)
+{
+ int v = 1 << 31;
+ etch_int32* newobj = new_int32(v);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newobj);
+ CU_ASSERT_EQUAL(newobj->value,v);
+
+ etch_object_destroy(newobj);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+static void test_primitive_int64(void)
+{
+ int64 v = ((int64)(1)) << 63;
+ etch_int64* newobj = new_int64(v);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newobj);
+ CU_ASSERT_EQUAL(newobj->value,v);
+
+ etch_object_destroy(newobj);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+static void test_primitive_float(void)
+{
+ float v = (float)(3.14159);
+ etch_float* newobj = new_float(v);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newobj);
+ CU_ASSERT_EQUAL(newobj->value,v);
+
+ etch_object_destroy(newobj);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+static void test_primitive_double(void)
+{
+ double v = (1 << 31) + 3.14159;
+ etch_double* newobj = new_double(v);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newobj);
+ CU_ASSERT_EQUAL(newobj->value,v);
+
+ etch_object_destroy(newobj);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+static void test_primitive_string(void)
+{
+ wchar_t* v = L"it works!";
+ etch_string* newobj = new_stringw(v);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newobj);
+ CU_ASSERT_EQUAL(wcscmp(v, newobj->v.valw), 0);
+
+ etch_object_destroy(newobj);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_nativearray_ctordtor()
+ * test that we can create and destroy an etch_nativearray with all memory accounted for
+ */
+static void test_nativearray_ctordtor(void)
+{
+ etch_nativearray* bytearray_1x4 = new_etch_nativearray(CLASSID_ARRAY_BYTE, sizeof(byte), 1, 4, 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(bytearray_1x4);
+
+ etch_object_destroy(bytearray_1x4);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_nativearray_1x1x4()
+ * test that we can populate and access a 1-dimensional array of byte
+ */
+static void test_nativearray_1x1x4(void)
+{
+ int i = 0, result = 0;
+ char x[4] = {'a','b','c','d'}, thisx = 0;
+ const int numdimensions = 1, itemcount = 4;
+
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, itemcount, 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ for(i = 0; i < itemcount; i++) /* populate array */
+ {
+ result = a->put1(a, &x[i], i); /* insert value of i to ith slot */
+ CU_ASSERT_EQUAL(result, 0);
+ }
+
+ for(i = 0; i < itemcount; i++) /* read values out of array */
+ {
+ result = a->get1(a, &thisx, i); /* get value of ith slot into thisx */
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(x[i], thisx);
+ }
+
+ etch_object_destroy(a);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_nativearray_1x4x4()
+ * test that we can populate and access a 1-dimensional array of int
+ */
+static void test_nativearray_1x4x4(void)
+{
+ int i = 0, j = 0, result = 0;
+ const int numdimensions = 1, itemcount = 4;
+
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_INT32, sizeof(int), numdimensions, itemcount, 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ for(i = 0; i < itemcount; i++) /* populate array */
+ {
+ result = a->put1(a, &i, i); /* insert value of i to ith slot */
+ CU_ASSERT_EQUAL(result, 0);
+ }
+
+ for(i = 0; i < itemcount; i++) /* read values out of array */
+ {
+ result = a->get1(a, &j, i); /* get value of ith slot into j */
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(i, j);
+ }
+
+ etch_object_destroy(a);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_nativearray_1xstruct()
+ * test that we can populate and access a 1-dimensional array of struct
+ */
+static void test_nativearray_1xstruct(void)
+{
+ int i = 0, result = 0;
+ const int numdimensions = 1, itemcount = 4;
+ struct x { int n; char c; };
+ struct x xgot = {-1,'?'};
+ struct x xs[4] = { {0,'a'}, {1,'b'}, {2,'c'}, {3,'d'}, };
+
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_STRUCT, sizeof(struct x), numdimensions, itemcount, 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ for(i = 0; i < itemcount; i++) /* populate array */
+ {
+ result = a->put1(a, &xs[i], i); /* insert xs[i] to ith slot */
+ CU_ASSERT_EQUAL(result, 0);
+ }
+
+ for(i = 0; i < itemcount; i++) /* read values out of array */
+ {
+ result = a->get1(a, &xgot, i); /* get value of ith slot into xgot */
+ CU_ASSERT_EQUAL(result, 0);
+ result = memcmp(&xgot, &xs[i], sizeof(struct x));
+ CU_ASSERT_EQUAL(result, 0);
+ }
+
+ etch_object_destroy(a);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_nativearray_2x1x4()
+ * test that we can populate and access a 2-dimensional array of byte
+ */
+static void test_nativearray_2x1x4(void)
+{
+ int i = 0, j = 0, result = 0;
+ char x[2][4] = { {'a','b','c','d'}, {'e','f','g','h'}, }, thisx = 0;
+ const int numdimensions = 2, dim0count = 4, dim1count = 2;
+
+ /* note when creating arrays, the dimensions are specified in reverse,
+ * low-order dimension (dim0) first, e.g., for x[2][3][4],
+ * dim0count is 4, dim1count is 3, dim2count is 2.
+ */
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, dim0count, dim1count, 0);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ for(i = 0; i < dim1count; i++) /* populate array */
+ {
+ for(j = 0; j < dim0count; j++)
+ { /* insert x[i][j] to array[i][j] */
+ result = a->put2(a, &x[i][j], i, j);
+ CU_ASSERT_EQUAL(result, 0);
+ }
+ }
+
+ for(i = 0; i < dim1count; i++) /* read array */
+ {
+ for(j = 0; j < dim0count; j++) /* read values out of array */
+ { /* get array[i][j] into thisx */
+ result = a->get2(a, &thisx, i, j);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(x[i][j], thisx);
+ }
+ }
+
+ etch_object_destroy(a);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_nativearray_3xstruct()
+ * test that we can populate and access a 3-dimensional array of struct
+ */
+static void test_nativearray_3xstruct(void)
+{
+ int i = 0, j = 0, k = 0, result = 0;
+ struct x { int n; char c; };
+ struct x xgot = {-1,'?'};
+ struct x *xthis = 0;
+
+ struct x xs[2][3][4] =
+ {
+ {
+ {
+ {0,'a'}, {1,'b'}, {2,'c'}, {3,'d'},
+ },
+ {
+ {0,'e'}, {1,'f'}, {2,'g'}, {3,'h'},
+ },
+ {
+ {0,'i'}, {1,'j'}, {2,'k'}, {3,'l'},
+ },
+ },
+ {
+ {
+ {0,'m'}, {1,'n'}, {2,'o'}, {3,'p'},
+ },
+ {
+ {0,'q'}, {1,'r'}, {2,'s'}, {3,'t'},
+ },
+ {
+ {0,'u'}, {1,'v'}, {2,'w'}, {3,'x'},
+ },
+ },
+ };
+
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+
+ etch_nativearray* a = new_etch_nativearray (CLASSID_ARRAY_STRUCT, sizeof(struct x),
+ numdimensions, dim0count, dim1count, dim2count);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ for(i = 0; i < dim2count; i++) /* write array */
+ {
+ for(j = 0; j < dim1count; j++)
+ {
+ for(k = 0; k < dim0count; k++)
+ {
+ result = a->put3(a, &xs[i][j][k], i, j, k);
+ CU_ASSERT_EQUAL(result, 0);
+ }
+ }
+ }
+
+ for(i = 0; i < dim2count; i++) /* read array */
+ {
+ for(j = 0; j < dim1count; j++)
+ {
+ for(k = 0; k < dim0count; k++)
+ {
+ result = a->get3(a, &xgot, i, j, k);
+ CU_ASSERT_EQUAL(result, 0);
+ xthis = &xs[i][j][k];
+ result = memcmp(&xgot, xthis, sizeof(struct x));
+ CU_ASSERT_EQUAL(result, 0);
+ }
+ }
+ }
+
+ etch_object_destroy(a);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_arrayfrom_2x1x4()
+ * test that we can access a static 2-dimensional array of byte.
+ * also validates that our array subscripting calculations match those of
+ * the C compiler, since we map and access an array mapped by the compiler.
+ * also validates that the etch_nativearray will not attempt to destroy
+ * the byte vector of an array created in this manner.
+ */
+static void test_arrayfrom_2x1x4(void)
+{
+ int i = 0, j = 0, result = 0;
+ char x[2][4] = { {'a','b','c','d'}, {'e','f','g','h'}, }, thisx = 0;
+ const int numdimensions = 2, dim0count = 4, dim1count = 2;
+
+ etch_nativearray* a = new_etch_nativearray_from(&x, CLASSID_ARRAY_BYTE,
+ sizeof(byte), numdimensions, dim0count, dim1count, 0);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ for(i = 0; i < dim1count; i++) /* read array */
+ {
+ for(j = 0; j < dim0count; j++) /* read values out of array */
+ { /* get array[i][j] into thisx */
+ result = a->get2(a, &thisx, i, j);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(x[i][j], thisx);
+ }
+ }
+
+ etch_object_destroy(a);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_subarray()
+ * test that we can create a one-dimensional subarray from a 2-dimensional
+ * array of byte, and that all memory is accounted for.
+ */
+static void test_subarray(void)
+{
+ int i = 0, result = 0;
+ char x[2][4] = { {'a','b','c','d'}, {'e','f','g','h'}, }, thisx = 0;
+ const int numdimensions = 2, dim0count = 4, dim1count = 2;
+ etch_nativearray *a0 = NULL, *a1 = NULL, *a2 = NULL;
+
+ etch_nativearray* a = new_etch_nativearray_from(&x, CLASSID_ARRAY_BYTE,
+ sizeof(byte), numdimensions, dim0count, dim1count, 0);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+ a->content_obj_type = ETCHTYPEB_BYTE;
+ a->content_class_id = CLASSID_NONE; /* unwrapped content */
+
+ a0 = new_subarray(a, 0); /* test index 0 */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a0);
+
+ CU_ASSERT_EQUAL(a0->is_content_owned, FALSE);
+ CU_ASSERT_EQUAL(a0->numdims, a->numdims-1);
+ CU_ASSERT_EQUAL(a0->bytecount, a->bytecount/2);
+ CU_ASSERT_EQUAL(a0->itemsize, a->itemsize);
+
+ CU_ASSERT_EQUAL(a0->content_obj_type, a->content_obj_type);
+ CU_ASSERT_EQUAL(a0->content_class_id, a->content_class_id);
+
+ /* recall that dimension and dimsize are stored low-order
+ * dimension first, so dimension[0] and dimsize[0] are
+ * the same for byte x[2][4] as for x[4] */
+ CU_ASSERT_EQUAL(a0->dimension[0], a->dimension[0]);
+ CU_ASSERT_EQUAL(a0->dimsize[0], a->dimsize[0]);
+
+ for(i = 0; i < dim0count; i++)
+ {
+ result = a0->get1(a0, &thisx, i);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(x[0][i], thisx);
+ }
+
+ a1 = new_subarray(a, 1); /* test index 1 */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a1);
+
+ CU_ASSERT_EQUAL(a1->is_content_owned, FALSE);
+ CU_ASSERT_EQUAL(a1->numdims, a->numdims-1);
+ CU_ASSERT_EQUAL(a1->bytecount, a->bytecount/2);
+ CU_ASSERT_EQUAL(a1->itemsize, a->itemsize);
+
+ CU_ASSERT_EQUAL(a1->content_obj_type, a->content_obj_type);
+ CU_ASSERT_EQUAL(a1->content_class_id, a->content_class_id);
+
+ /* recall that dimension and dimsize are stored low-order
+ * dimension first, so dimension[0] and dimsize[0] are
+ * the same for byte x[2][4] as for x[4] */
+ CU_ASSERT_EQUAL(a1->dimension[0], a->dimension[0]);
+ CU_ASSERT_EQUAL(a1->dimsize[0], a->dimsize[0]);
+
+ for(i = 0; i < dim0count; i++)
+ {
+ result = a1->get1(a1, &thisx, i);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(x[1][i], thisx);
+ }
+
+ a2 = new_subarray(a, 2); /* test nonexistent index 2 */
+ CU_ASSERT_PTR_NULL(a2);
+
+ etch_object_destroy(a1);
+ etch_object_destroy(a0);
+ etch_object_destroy(a);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_if_busted_subarray()
+ * this test is here to debug something that was either broken in subarray,
+ * or that was not broken, but rather the validator test using it was broken.
+ * verdict: subarray itemsize was broken, is fixed.
+ */
+static void test_if_busted_subarray(void)
+{
+ short x[2][3][4] =
+ { { { 1,1,1,1, }, { 1,1,1,1, }, { 1,-1,32767, -32768, }, },
+ { { 1,1,1,1, }, { 1,1,1,1, }, { 1,-1,0xfffe,0xffff, }, },
+ };
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+ int i=0, j=0, k=0;
+
+ etch_nativearray* a1 = new_etch_nativearray_from(&x, CLASSID_ARRAY_INT16,
+ sizeof(short), numdimensions, dim0count, dim1count, dim2count);
+ a1->content_obj_type = ETCHTYPEB_INT16;
+ a1->content_class_id = CLASSID_NONE; /* unwrapped */
+
+ for(i = 0; i < dim2count; i++)
+ {
+ etch_nativearray* a2 = (etch_nativearray*) etch_nativearray_get_element(a1, i);
+ CU_ASSERT_EQUAL_FATAL(is_etch_nativearray(a2), TRUE);
+ CU_ASSERT_EQUAL_FATAL(a2->bytecount, a1->bytecount/a1->dimension[2]);
+ CU_ASSERT_EQUAL_FATAL(a2->numdims, a1->numdims-1);
+ CU_ASSERT_EQUAL_FATAL(a2->dimsize[0], a1->dimsize[0]);
+ CU_ASSERT_EQUAL_FATAL(a2->dimsize[1], a1->dimsize[1]);
+ CU_ASSERT_EQUAL_FATAL(a2->dimension[0],a1->dimension[0]);
+ CU_ASSERT_EQUAL_FATAL(a2->dimension[1],a1->dimension[1]);
+ CU_ASSERT_EQUAL_FATAL(a2->dimension[1],dim1count);
+
+ for(j = 0; j < dim1count; j++)
+ {
+ etch_nativearray* a3 = (etch_nativearray*) etch_nativearray_get_element(a2, j);
+ CU_ASSERT_EQUAL_FATAL(is_etch_nativearray(a3), TRUE);
+ CU_ASSERT_EQUAL_FATAL(a3->bytecount, a2->bytecount/a2->dimension[1]);
+ CU_ASSERT_EQUAL_FATAL(a3->numdims, a2->numdims-1);
+ CU_ASSERT_EQUAL_FATAL(a3->dimsize[0], a2->dimsize[0]);
+ CU_ASSERT_EQUAL_FATAL(a3->dimension[0],a2->dimension[0]);
+ CU_ASSERT_EQUAL_FATAL(a3->dimension[0],dim0count);
+
+ for(k = 0; k < dim0count; k++)
+ {
+ short n_expected = 0, n_actual = 0;
+ etch_int16* shortobj = (etch_int16*) etch_nativearray_get_element(a3, k);
+ CU_ASSERT_EQUAL_FATAL(is_etch_int16(shortobj), TRUE);
+ n_expected = x[i][j][k];
+ n_actual = shortobj->value;
+ CU_ASSERT_EQUAL_FATAL(n_expected, n_actual);
+
+ etch_object_destroy(shortobj);
+ }
+
+ etch_object_destroy(a3);
+ }
+
+ etch_object_destroy(a2);
+ }
+
+ etch_object_destroy(a1);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_get_element()
+ * test that we can get element[i] of a native array, and that element
+ * is another native array, or a wrapped etch object the same type as
+ * the array's content_obj_type and possibly content_class_id.
+ */
+static void test_get_element(void)
+{
+ int i = 0;
+ char x[2][4] = { {'a','b','c','d'}, {'e','f','g','h'}, }, thisx = 0;
+ const int numdimensions = 2, dim0count = 4, dim1count = 2;
+ etch_nativearray *a1 = NULL;
+ etch_object* returnobj = NULL;
+ etch_byte* retbyteobj = NULL;
+
+ etch_nativearray* a = new_etch_nativearray_from(&x, CLASSID_ARRAY_BYTE,
+ sizeof(byte), numdimensions, dim0count, dim1count, 0);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+ a->content_obj_type = ETCHTYPEB_BYTE;
+ a->content_class_id = CLASSID_NONE; /* unwrapped content */
+
+ /* get element[1] from the array, expecting a byte[4] native array */
+ returnobj = etch_nativearray_get_element(a, 1);
+
+ CU_ASSERT_EQUAL_FATAL(is_etch_nativearray(returnobj), TRUE);
+
+ a1 = (etch_nativearray*) returnobj;
+
+ CU_ASSERT_EQUAL(a1->is_content_owned, FALSE);
+ CU_ASSERT_EQUAL(a1->numdims, a->numdims-1);
+ CU_ASSERT_EQUAL(a1->bytecount, a->bytecount/2);
+ CU_ASSERT_EQUAL(a1->itemsize, a->itemsize);
+
+ CU_ASSERT_EQUAL(a1->content_obj_type, a->content_obj_type);
+ CU_ASSERT_EQUAL(a1->content_class_id, a->content_class_id);
+ CU_ASSERT_EQUAL(a1->dimension[0], a->dimension[0]);
+ CU_ASSERT_EQUAL(a1->dimsize[0], a->dimsize[0]);
+
+ for(i = 0; i < dim0count; i++)
+ {
+ /* get element[i] from the subarray, expecting an etch_byte object */
+ returnobj = etch_nativearray_get_element(a1, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(returnobj);
+ CU_ASSERT_EQUAL_FATAL(((etch_object*)returnobj)->class_id, CLASSID_PRIMITIVE_BYTE);
+
+ retbyteobj = (etch_byte*) returnobj; /* verify that wrapped byte */
+ thisx = retbyteobj->value; /* matches original array */
+ CU_ASSERT_EQUAL(x[1][i], thisx);
+
+ etch_object_destroy(retbyteobj);
+ }
+
+ etch_object_destroy(a1);
+ etch_object_destroy(a);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etchobject_suite()
+{
+ CU_pSuite ps = CU_add_suite("etchobject", init_suite, clean_suite);
+
+ CU_add_test(ps, "test primitive byte", test_primitive_byte);
+ CU_add_test(ps, "test primitive boolean", test_primitive_bool);
+ CU_add_test(ps, "test primitive int8", test_primitive_int8);
+ CU_add_test(ps, "test primitive int16", test_primitive_int16);
+ CU_add_test(ps, "test primitive int32", test_primitive_int32);
+ CU_add_test(ps, "test primitive int64", test_primitive_int64);
+ CU_add_test(ps, "test primitive float", test_primitive_float);
+ CU_add_test(ps, "test primitive double", test_primitive_double);
+ CU_add_test(ps, "test primitive string", test_primitive_string);
+
+ CU_add_test(ps, "test tri-level inheritance", test_inheritance);
+ CU_add_test(ps, "test clone and auto-construct superclass", test_clone);
+
+ CU_add_test(ps, "test native array ctor", test_nativearray_ctordtor);
+ CU_add_test(ps, "test 1-dim byte array", test_nativearray_1x1x4);
+ CU_add_test(ps, "test 1-dim int array", test_nativearray_1x4x4);
+ CU_add_test(ps, "test 1-dim struct array", test_nativearray_1xstruct);
+ CU_add_test(ps, "test 2-dim byte array", test_nativearray_2x1x4);
+ CU_add_test(ps, "test 3-dim struct array", test_nativearray_3xstruct);
+ CU_add_test(ps, "test static 2-dim byte array", test_arrayfrom_2x1x4);
+ CU_add_test(ps, "test subarray", test_subarray);
+ CU_add_test(ps, "test get array element", test_get_element);
+ CU_add_test(ps, "test 3-dim int16 subarray ", test_if_busted_subarray);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_flexbuf.c b/binding-c/runtime/c/src/test/common/test_flexbuf.c
new file mode 100644
index 0000000..abc8db3
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_flexbuf.c
@@ -0,0 +1,396 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * test_flexbuf.c
+ * test flex buffer
+ */
+#include "etch_runtime.h"
+#include "etch_flexbuffer.h"
+#include "etch_mem.h"
+
+#include <limits.h>
+#include <float.h>
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+static etch_flexbuffer* fbuf = 0;
+
+// TODO add testcases
+// test endianes big + little
+// test signed + unsigned values
+
+/**
+ * returns number of errors
+ */
+static int check_buf(etch_flexbuffer* buf, const int len, const int ndx, const int avl)
+{
+ int errors = 0, avail = (int) etch_flexbuf_avail(buf);
+ if (buf->datalen != len) errors++;
+ if (buf->index != ndx) errors++;
+ if (avail != avl) errors++;
+ return errors;
+}
+
+static void testCreateAndDestroy0(void)
+{
+ fbuf = new_flexbuffer(0);
+ CU_ASSERT_PTR_NOT_NULL(fbuf);
+
+ CU_ASSERT(etch_flexbuf_avail(fbuf) == 0);
+
+ destroy_etch_flexbuffer(fbuf);
+}
+
+static void testCreateAndDestroy1(void)
+{
+ fbuf = new_flexbuffer(-1);
+ CU_ASSERT_PTR_NOT_NULL(fbuf);
+ destroy_etch_flexbuffer(fbuf);
+
+ fbuf = new_flexbuffer(1);
+ CU_ASSERT_PTR_NOT_NULL(fbuf);
+ CU_ASSERT(etch_flexbuf_avail(fbuf) == 0);
+
+ destroy_etch_flexbuffer(fbuf);
+
+ fbuf = new_flexbuffer(4*1024*1024+1);
+ CU_ASSERT_PTR_NOT_NULL(fbuf);
+ destroy_etch_flexbuffer(fbuf); /* takes NULL */
+}
+
+static void testCreateAndDestroy2(void)
+{
+ void *ptr = etch_malloc(1024, 0);
+ fbuf = etch_flexbuf_create_b(ptr, 1024, 0);
+ CU_ASSERT_PTR_NOT_NULL(fbuf);
+ CU_ASSERT(etch_flexbuf_avail(fbuf) == 0);
+
+ destroy_etch_flexbuffer(fbuf);
+}
+
+static void testCreateAndDestroy3(void)
+{
+ void *ptr = etch_malloc(1024, 0);
+ fbuf = etch_flexbuf_create_bi(ptr, 1024, 100, 0);
+ CU_ASSERT_PTR_NOT_NULL(fbuf);
+ CU_ASSERT(etch_flexbuf_avail(fbuf) == 100);
+
+ destroy_etch_flexbuffer(fbuf);
+}
+
+static void testPutAndGet(void)
+{
+ byte test_bytes[] = "testbytes";
+ void *test_buffer = etch_malloc(128, 0);
+ size_t n;
+ int i;
+ etch_flexbuffer *efb2;
+
+ fbuf = new_flexbuffer(0);
+
+ etch_flexbuf_put(fbuf, test_bytes, 0, strlen((char*)test_bytes));
+
+ /* rewind the buffer */
+ etch_flexbuf_set_index(fbuf, 0);
+ n = etch_flexbuf_get(fbuf, test_buffer, 0, strlen((char*)test_bytes));
+ CU_ASSERT(memcmp(test_buffer, test_bytes, n) == 0);
+
+ /* test put byte */
+ etch_flexbuf_set_index(fbuf, 0);
+ for(i = 0; i < (int) strlen((char*)test_bytes); i++)
+ etch_flexbuf_put_byte(fbuf, test_bytes[i]);
+
+ etch_flexbuf_set_index(fbuf, 0);
+ n = etch_flexbuf_get(fbuf, test_buffer, 0, strlen((char*)test_bytes));
+ CU_ASSERT(memcmp(test_buffer, test_bytes, n) == 0);
+
+ /* test put from another buffer */
+ efb2 = new_flexbuffer(0);
+ etch_flexbuf_set_index(fbuf, 0);
+ etch_flexbuf_put_from(efb2, fbuf, n);
+
+ etch_flexbuf_set_index(efb2, 0);
+ n = etch_flexbuf_get(efb2, test_buffer, 0, strlen((char*)test_bytes));
+ CU_ASSERT(memcmp(test_buffer, test_bytes, n) == 0);
+
+ etch_free(test_buffer);
+ destroy_etch_flexbuffer(fbuf);
+ destroy_etch_flexbuffer(efb2);
+}
+
+static void testPutAndGetFully(void)
+{
+ byte test_bytes[] = "testbytes";
+ void *test_buffer = etch_malloc(128, 0);
+ size_t n;
+
+ fbuf = new_flexbuffer(0);
+
+ etch_flexbuf_put(fbuf, test_bytes, 0, strlen((char*)test_bytes));
+
+ /* rewind the buffer */
+ etch_flexbuf_set_index(fbuf, 0);
+ n = etch_flexbuf_get_fully(fbuf, test_buffer, strlen((char*)test_bytes));
+ CU_ASSERT(memcmp(test_buffer, test_bytes, n) == 0);
+
+ etch_flexbuf_set_index(fbuf, 0);
+ n = etch_flexbuf_get_fully(fbuf, test_buffer, 4);
+ CU_ASSERT(memcmp(test_buffer, test_bytes, 4) == 0);
+
+ etch_flexbuf_set_index(fbuf, 0);
+ n = etch_flexbuf_get_fully(fbuf, test_buffer, 32);
+
+ CU_ASSERT(memcmp(test_buffer, test_bytes, (strlen((char*)test_bytes) < 32 ? strlen((char*)test_bytes): 32)) == 0);
+
+ etch_free(test_buffer);
+ destroy_etch_flexbuffer(fbuf);
+}
+
+
+static void testPutAndGetByte(void)
+{
+ byte test_bytes[] = "testbytes";
+ void *test_buffer = etch_malloc(128, 0);
+ size_t n;
+ int i;
+
+ fbuf = new_flexbuffer(0);
+
+ etch_flexbuf_set_index(fbuf, 0);
+ for(i = 0; i < (int)strlen((char*)test_bytes); i++)
+ etch_flexbuf_put_byte(fbuf, test_bytes[i]);
+
+ etch_flexbuf_set_index(fbuf, 0);
+ n = etch_flexbuf_get(fbuf, test_buffer, 0, strlen((char*)test_bytes));
+ CU_ASSERT(memcmp(test_buffer, test_bytes, n) == 0);
+
+
+ etch_free(test_buffer);
+ destroy_etch_flexbuffer(fbuf);
+}
+
+static void testPutAndGetShort(void)
+{
+ short test_shorts[] = {SHRT_MIN, SHRT_MAX, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0x7FFF, \
+ 0xFFFF, 0xFFFE, -8192, -4096, -2048, -1024, -512, -256, -128, -64, -32, -16, -8, -4, -2, -1};
+
+ int i, result; short value = 0;
+
+ fbuf = new_flexbuffer(0);
+
+ for(i = 0; i < sizeof(test_shorts)/sizeof(short); i++)
+ etch_flexbuf_put_short(fbuf, test_shorts[i]);
+
+ etch_flexbuf_set_index(fbuf, 0);
+
+ for(i = 0; i < sizeof(test_shorts)/sizeof(short); i++)
+ {
+ result = etch_flexbuf_get_short(fbuf, &value);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(test_shorts[i], value);
+ }
+
+ destroy_etch_flexbuffer(fbuf);
+}
+
+
+static void testPutAndGetInt(void)
+{
+ int test_ints[] = {INT_MIN, INT_MAX, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0x7FFF, \
+ 0xFFFF, 0xFFFFF, 0xFFFFFF, 0xFFFFFFF, 0xFFFFFFE,0x7FFFFFFF, 0xFFFFFFFF, 0xF7FFFFFF, 0xF6FFFFFF, \
+ 0xF5FFFFFF, 0xF4FFFFFF, 0xF3FFFFFF, 0xF2FFFFFF, 0xF1FFFFFF, 0xF0FFFFFF, 0xFF0FFFFF, \
+ -8192, -4096, -2048, -1024, -512, -256, -128, -64, -32, -16, -8, -4, -2, -1};
+
+ int i;
+
+ fbuf = new_flexbuffer(0);
+
+ for(i = 0; i < sizeof(test_ints)/sizeof(int); i++)
+ etch_flexbuf_put_int(fbuf, test_ints[i]);
+
+ etch_flexbuf_set_index(fbuf, 0);
+ for(i = 0; i < sizeof(test_ints)/sizeof(int); i++)
+ {
+ int n = 0, m = test_ints[i], result;
+ result = etch_flexbuf_get_int(fbuf, &n);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(n,m);
+ }
+
+ destroy_etch_flexbuffer(fbuf);
+}
+
+
+static void testPutAndGetInt64(void)
+{
+ int64 test_int64s[] = {LONG_MIN, LONG_MAX, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0x7FFF, \
+ 0xFFFF, 0xFFFFF, 0xFFFFFF, 0xFFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, 0xF7FFFFFF, 0xF6FFFFFF, 0x7FFFFFFFFFFFFFFFLL,\
+ 0xFFFFFFFFFFFFFFFFLL, 0xFFFFFFFFFFFFFF0LL, 0xFFFFFFFFFFFFFF00LL, 0xFFFFFFFFFFFFF000LL, 0xFFFFFFFFFFFFF000LL, \
+ 0xF5FFFFFF, 0xF4FFFFFF, 0xF3FFFFFF, 0xF2FFFFFF, 0xF1FFFFFF, 0xF0FFFFFF, 0xFF0FFFFF, \
+ -8192, -4096, -2048, -1024, -512, -256, -128, -64, -32, -16, -8, -4, -2, -1};
+
+ int i, result;
+
+ fbuf = new_flexbuffer(0);
+
+ for(i = 0; i < sizeof(test_int64s)/sizeof(int64); i++)
+ etch_flexbuf_put_long(fbuf, test_int64s[i]);
+
+ etch_flexbuf_set_index(fbuf, 0);
+
+ for(i = 0; i < sizeof(test_int64s)/sizeof(int64); i++)
+ {
+ int64 n = 0, m = test_int64s[i];
+ result = etch_flexbuf_get_long(fbuf, &n);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(n,m);
+ }
+
+ destroy_etch_flexbuffer(fbuf);
+}
+
+
+static void testPutAndGetFloat(void)
+{
+ float test_floats[] = {FLT_MIN, 0.0f, 1.1f, 2.2f, 4.4f, 8.8f, 16.16f, 32.32f, 64.64f, 128.128f, 256.256f, 512.512f,\
+ 1024.1024f, 2048.2048f, 4096.4096f, 8192.8192f, FLT_MAX};
+
+ int i, result;
+
+ fbuf = new_flexbuffer(0);
+
+ for(i = 0; i < sizeof(test_floats)/sizeof(float); i++)
+ etch_flexbuf_put_float(fbuf, test_floats[i]);
+
+ etch_flexbuf_set_index(fbuf, 0);
+
+ for(i = 0; i < sizeof(test_floats)/sizeof(float); i++)
+ {
+ float x = 0.0; double diff; int is_equal;
+ const float y = test_floats[i];
+ result = etch_flexbuf_get_float(fbuf, &x);
+ CU_ASSERT_EQUAL(result, 0);
+ diff = (double) (y - x);
+ is_equal = fabs(diff) < 0.00001;
+ CU_ASSERT_EQUAL(is_equal, TRUE);
+ }
+
+
+ destroy_etch_flexbuffer(fbuf);
+}
+
+static void testPutAndGetDouble(void)
+{
+ double test_doubles[] = {DBL_MIN, 0.0, 1.1, 2.2, 4.4, 8.8, 16.16, 32.32, 64.64, 128.128, 256.256, 512.512,\
+ 1024.1024, 2048.2048, 4096.4096, 8192.8192, DBL_MAX};
+
+ int i, result;
+
+ fbuf = new_flexbuffer(0);
+
+ for(i = 0; i < sizeof(test_doubles)/sizeof(double); i++)
+ etch_flexbuf_put_double(fbuf, test_doubles[i]);
+
+ etch_flexbuf_set_index(fbuf, 0);
+
+ for(i = 0; i < sizeof(test_doubles)/sizeof(double); i++)
+ {
+ double x, y = test_doubles[i];
+ result = etch_flexbuf_get_double(fbuf, &x);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT(x == y);
+ }
+
+ destroy_etch_flexbuffer(fbuf);
+}
+
+static void testSetSize(void)
+{
+ etch_flexbuffer *fbuf = new_flexbuffer(0);
+
+ CU_ASSERT_EQUAL(0, etch_flexbuf_set_length(fbuf, 1024));
+ CU_ASSERT_EQUAL(0, check_buf(fbuf, 1024, 0, 1024));
+
+ CU_ASSERT_EQUAL(0, etch_flexbuf_set_length(fbuf, 0));
+ CU_ASSERT_EQUAL(0, check_buf(fbuf, 0, 0, 0));
+
+ destroy_etch_flexbuffer(fbuf);
+}
+
+static void testCompact(void)
+{
+ int i;
+ etch_flexbuffer *fbuf = new_flexbuffer(128);
+
+ for(i = 0; i < 10; i++)
+ etch_flexbuf_put_short(fbuf, 1);
+
+ etch_flexbuf_set_index(fbuf, 0);
+ etch_flexbuf_compact(fbuf);
+ CU_ASSERT(fbuf->index ==0);
+ CU_ASSERT(etch_flexbuf_avail(fbuf) == (10*sizeof(short)));
+
+ destroy_etch_flexbuffer(fbuf);
+}
+
+
+//int _tmain(int argc, _TCHAR* argv[])
+CU_pSuite test_etch_flexbuf_suite()
+{
+ CU_pSuite ps = CU_add_suite("suite flexbuffer", init_suite, clean_suite);
+
+ CU_add_test(ps, "testCreateAndDestroy0", testCreateAndDestroy0);
+ CU_add_test(ps, "testCreateAndDestroy1", testCreateAndDestroy1);
+ CU_add_test(ps, "testCreateAndDestroy2", testCreateAndDestroy2);
+ CU_add_test(ps, "testCreateAndDestroy3", testCreateAndDestroy3);
+ CU_add_test(ps, "testPutAndGet", testPutAndGet);
+ CU_add_test(ps, "testPutAndGetFully", testPutAndGetFully);
+ CU_add_test(ps, "testPutAndGetByte", testPutAndGetByte);
+ CU_add_test(ps, "testPutAndGetShort", testPutAndGetShort);
+ CU_add_test(ps, "testPutAndGetInt", testPutAndGetInt);
+ CU_add_test(ps, "testPutAndGetInt64", testPutAndGetInt64);
+ CU_add_test(ps, "testPutAndGetFloat", testPutAndGetFloat);
+ CU_add_test(ps, "testPutAndGetFloat", testPutAndGetDouble);
+ CU_add_test(ps, "testSetSize", testSetSize);
+ CU_add_test(ps, "testCompact", testCompact);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_hashing.c b/binding-c/runtime/c/src/test/common/test_hashing.c
new file mode 100644
index 0000000..80e1d5b
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_hashing.c
@@ -0,0 +1,365 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_hashing.c
+ * test hashing of various complex items and associated memory management
+ */
+
+#include "etch_runtime.h"
+#include "etch_binary_tdi.h" /* must be included second */
+#include "etch_binary_tdo.h"
+#include "etch_connection.h"
+#include "etch_id_name.h"
+#include "etch_field.h"
+#include "etch_type.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+// intern types
+static int g_is_automated_test;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * individual setup and teardown
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+static int this_test_setup()
+{
+ int result = -1;
+
+ do {
+ result = 0;
+
+ } while(0);
+
+ return result;
+}
+
+static int this_test_teardown()
+{
+
+ return 0;
+}
+
+/**
+ * destroy_content_id_name()
+ * passed a content pointer out of the hashtable, interprets pointer as etch_id_name,
+ * frees the name string, and frees the etch_id_name shell.
+ */
+static int destroy_content_id_name(void* content)
+{
+ etch_id_name* idname = (etch_id_name*) content;
+ return idname? etch_object_destroy(idname): -1;
+ return 0;
+}
+
+
+/**
+ * destroy_content_field()
+ */
+static int destroy_content_field(void* content)
+{
+ etch_field* field = (etch_field*) content;
+ return field? etch_object_destroy(field): -1;
+}
+
+
+/**
+ * destroy_content_type()
+ */
+static int destroy_content_type(void* content)
+{
+ etch_type* type = (etch_type*) content;
+ return type? etch_object_destroy(type): -1;
+}
+
+
+/**
+ * test_idname_as_key
+ * tests that we can use etch_id_name (and etch_field and etch_type, since they
+ * are etch_id_name typedefs), as hashkeys, and subsequently acccess the original
+ * objects, individually clean up the keys, and ask hashtable to clean up the values.
+ */
+static void test_idname_as_key_hashclean_values(void)
+{
+ etch_field* my_field = NULL;
+ etch_type* my_type = NULL;
+ etch_id_name* my_idname = NULL;
+
+ const int id_name_size = sizeof(etch_id_name);
+ const int field_size = sizeof(etch_field);
+ const int type_size = sizeof(etch_type);
+
+ etch_hashtable* myhashtab = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* myentry = &hashbucket;
+
+ wchar_t* name1 = L"name number one";
+ wchar_t* name2 = L"nombre numero dos";
+ wchar_t* name3 = L"le troisieme nom";
+
+ const size_t numElements1 = wcslen(name1);
+ const size_t numElements2 = wcslen(name2);
+ const size_t numElements3 = wcslen(name3);
+
+ const size_t numBytes1 = sizeof(wchar_t) * numElements1;
+ const size_t numBytes2 = sizeof(wchar_t) * numElements2;
+ const size_t numBytes3 = sizeof(wchar_t) * numElements3;
+
+ void* value1 = NULL, *value2 = NULL, *value3 = NULL;
+
+ int result = 0, result1 = 0, result2 = 0, result3 = 0;
+ unsigned bytes_allocated = 0;
+
+ value1 = etch_malloc(numBytes1 + sizeof(wchar_t), 0);
+ value2 = etch_malloc(numBytes2 + sizeof(wchar_t), 0);
+ value3 = etch_malloc(numBytes3 + sizeof(wchar_t), 0);
+
+ wcscpy(value1, name1);
+ wcscpy(value2, name2);
+ wcscpy(value3, name3);
+
+ myhashtab = new_hashtable(16);
+ myhashtab->is_readonly_keys = TRUE; /* keys will NOT be freed on destroy */
+ myhashtab->is_readonly_values = FALSE; /* values WILL be freed on destroy */
+ myhashtab->is_tracked_memory = TRUE; /* hashtable will use etch_free */
+
+ my_idname = new_id_name(name1);
+ my_field = new_field(name2);
+ my_type = new_type(name3);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_idname);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_field);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_type);
+
+ /* ensure constructors populated the hash key */
+ CU_ASSERT_NOT_EQUAL_FATAL(((etch_object*)my_idname)->get_hashkey(my_idname),0);
+ CU_ASSERT_NOT_EQUAL_FATAL(((etch_object*)my_field)->get_hashkey(my_field),0);
+ CU_ASSERT_NOT_EQUAL_FATAL(((etch_object*)my_type)->get_hashkey(my_type),0);
+
+ /* ensure constructors populated the wire id */
+ CU_ASSERT_NOT_EQUAL_FATAL(my_idname->id,0);
+ CU_ASSERT_NOT_EQUAL_FATAL(my_field->id,0);
+ CU_ASSERT_NOT_EQUAL_FATAL(my_type->id,0);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->inserth(myhashtab->realtable, my_idname, value1, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->inserth(myhashtab->realtable, my_field, value2, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->inserth(myhashtab->realtable, my_type, value3, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->findh(myhashtab->realtable, ((etch_object*)my_idname)->get_hashkey(my_idname), myhashtab, &myentry);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL(myentry->key);
+ CU_ASSERT_PTR_NOT_NULL(myentry->value);
+ result = destroy_content_id_name(myentry->key);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->findh(myhashtab->realtable, ((etch_object*)my_field)->hashkey, myhashtab, &myentry);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL(myentry->key);
+ CU_ASSERT_PTR_NOT_NULL(myentry->value);
+ result = destroy_content_field(myentry->key);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->findh(myhashtab->realtable, ((etch_object*)my_type)->hashkey, myhashtab, &myentry);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL(myentry->key);
+ CU_ASSERT_PTR_NOT_NULL(myentry->value);
+ result = destroy_content_type(myentry->key);
+ CU_ASSERT_EQUAL(result,0);
+
+ /** above we looked up each item and freed memory for the id_name, field, and type keys,
+ * but not for their values. when we created the map we specified is_readonly_keys, so the
+ * map destructor will free memory for the value content, but not for the key content. */
+ etch_object_destroy(myhashtab);
+
+}
+
+
+/**
+ * test_idname_as_key
+ * tests that we can use etch_id_name (and etch_field and etch_type, since they
+ * are etch_id_name typedefs), as hashkeys, and subsequently acccess the original
+ * objects and clean up both the keys and values.
+ */
+static void test_idname_as_key_hashclean_none(void)
+{
+ etch_field* my_field = NULL;
+ etch_type* my_type = NULL;
+ etch_id_name* my_idname = NULL;
+
+ const int id_name_size = sizeof(etch_id_name);
+ const int field_size = sizeof(etch_field);
+ const int type_size = sizeof(etch_type);
+
+ etch_hashtable* myhashtab = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* myentry = &hashbucket;
+
+ wchar_t* name1 = L"name number one";
+ wchar_t* name2 = L"nombre numero dos";
+ wchar_t* name3 = L"le troisieme nom";
+
+ const size_t numElements1 = wcslen(name1);
+ const size_t numElements2 = wcslen(name2);
+ const size_t numElements3 = wcslen(name3);
+
+ const size_t numBytes1 = sizeof(wchar_t) * numElements1;
+ const size_t numBytes2 = sizeof(wchar_t) * numElements2;
+ const size_t numBytes3 = sizeof(wchar_t) * numElements3;
+ void* value1 = NULL, *value2 = NULL, *value3 = NULL;
+
+ size_t actlen1 = 0, actlen2 = 0, actlen3 = 0;
+ int result = 0, result1 = 0, result2 = 0, result3 = 0;
+ unsigned bytes_allocated = 0;
+
+ value1 = etch_malloc(numBytes1 + 2, 0);
+ value2 = etch_malloc(numBytes2 + 2, 0);
+ value3 = etch_malloc(numBytes3 + 2, 0);
+
+// wcscpy_s(value1, numElements1+1, name1);
+// wcscpy_s(value2, numElements2+1, name2);
+// wcscpy_s(value3, numElements3+1, name3);
+
+ wcscpy(value1, name1);
+ wcscpy(value2, name2);
+ wcscpy(value3, name3);
+
+
+ actlen1 = wcslen(name1); actlen2 = wcslen(name2); actlen3 = wcslen(name3);
+
+ myhashtab = new_hashtable(16);
+ myhashtab->is_readonly_keys = TRUE; /* keys will be freed on destroy */
+ myhashtab->is_readonly_values = TRUE; /* values will be freed on destroy */
+ myhashtab->is_tracked_memory = TRUE; /* hashtable will etch_free content */
+
+ /* here we are creating id_name, field, and type objects using names allocated
+ * on out local stack. therefore the cleanup parameters must be set to not free
+ * memory for the name part, or we will crash. note that name is part of the
+ * key structs, and is also the value of the hashed key/value pair, however
+ * we etch_malloc'ed memory for the values and copied our stack strings into it,
+ * so we will want to free the value memory. we could ask the hashtable to do
+ * so but we'll do it manually here instead.
+ */
+ my_idname = new_id_name(name1);
+ my_field = new_field(name2);
+ my_type = new_type(name3);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_idname);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_field);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_type);
+
+ /* ensure constructors populated the hash key */
+ CU_ASSERT_NOT_EQUAL_FATAL(((etch_object*)my_idname)->hashkey,0);
+ CU_ASSERT_NOT_EQUAL_FATAL(((etch_object*)my_field)->hashkey,0);
+ CU_ASSERT_NOT_EQUAL_FATAL(((etch_object*)my_type)->hashkey,0);
+
+ /* ensure constructors populated the id (hash of name) */
+ CU_ASSERT_NOT_EQUAL_FATAL(my_idname->id,0);
+ CU_ASSERT_NOT_EQUAL_FATAL(my_field->id,0);
+ CU_ASSERT_NOT_EQUAL_FATAL(my_type->id,0);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->inserth(myhashtab->realtable, my_idname, value1, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->inserth(myhashtab->realtable, my_field, value2, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->inserth(myhashtab->realtable, my_type, value3, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->findh(myhashtab->realtable, ((etch_object*)my_idname)->hashkey, myhashtab, &myentry);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL(myentry->key);
+ CU_ASSERT_PTR_NOT_NULL(myentry->value);
+ result = destroy_content_id_name(myentry->key);
+ CU_ASSERT_EQUAL(result,0);
+ etch_free(myentry->value);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->findh(myhashtab->realtable, ((etch_object*)my_field)->hashkey, myhashtab, &myentry);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL(myentry->key);
+ CU_ASSERT_PTR_NOT_NULL(myentry->value);
+ result = destroy_content_field(myentry->key);
+ CU_ASSERT_EQUAL(result,0);
+ etch_free(myentry->value);
+
+ result = ((struct i_hashtable*)((etch_object*)myhashtab)->vtab)->findh(myhashtab->realtable, ((etch_object*)my_type)->hashkey, myhashtab, &myentry);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL(myentry->key);
+ CU_ASSERT_PTR_NOT_NULL(myentry->value);
+ result = destroy_content_type(myentry->key);
+ CU_ASSERT_EQUAL(result,0);
+ etch_free(myentry->value);
+
+ etch_object_destroy(myhashtab); /* hashtable was asked to not free any content */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_hashing_suite()
+{
+ CU_pSuite ps = CU_add_suite("suite_hashing", init_suite, clean_suite);
+
+ CU_add_test(ps, "test hash id_name etc - auto-cleanup values", test_idname_as_key_hashclean_values);
+ CU_add_test(ps, "test hash id_name etc - no auto-cleanup", test_idname_as_key_hashclean_none);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_hashtable.c b/binding-c/runtime/c/src/test/common/test_hashtable.c
new file mode 100644
index 0000000..10aecbb
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_hashtable.c
@@ -0,0 +1,1179 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_hashtable.c
+ * tests the C implementation of etch hashtable.
+ * this hashtable implementation uses the public domain jenkins hashtable.
+ */
+#include "etch_runtime.h"
+#include "etch_hash.h"
+#include "etch_general.h"
+#include "etch_objecttypes.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define TESTDATAPATH "./jenktest.txt"
+#define MAXLINELEN 64
+#define DATASIZE 4
+#define DATAVALUE "foo"
+#define INITIAL_TABLE_SIZE 64
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+// intern types
+static int g_is_automated_test;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/**
+ * test_new_hashtable
+ * unit test for new_hashtable
+ */
+static void test_new_hashtable(void)
+{
+ /* new_hashtable always returns mimimal initial size if the input value is too small */
+ etch_hashtable* eht = new_hashtable(-1);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), ETCH_DEFAULT_HASHTABLE_SIZE);
+ etch_object_destroy(eht);
+
+ /* the hashtable size will be modified to previous power of 2 */
+ eht = new_hashtable(63);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32); /* size will be 3 2instead of 64 */
+ etch_object_destroy(eht);
+
+ /* good input case, the size is within boundary and power of 2 */
+ eht = new_hashtable(32);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+ etch_object_destroy(eht);
+
+ /* the maximum initial size is MAX_INITIAL_HASHTABLE_SIZE */
+ eht = new_hashtable(MAX_INITIAL_HASHTABLE_SIZE+1);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), MAX_INITIAL_HASHTABLE_SIZE);
+ etch_object_destroy(eht);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+static void test_map(void)
+{
+ etch_hashtable* map = new_etch_map(0);
+
+ int count = 0;
+
+ etch_int32* content1 = new_int32(1);
+ etch_string* content2 = new_stringa("Hallo");
+
+ etch_int32* key1 = (etch_int32*)new_stringa("Hallo");
+ etch_string* key2 = (etch_string*)new_int32(1);
+
+ count = etchmap_count(map);
+ CU_ASSERT_TRUE(count == 0);
+
+ etchmap_add(map, key1, content1);
+ etchmap_add(map, key2, content2);
+
+ count = etchmap_count(map);
+ CU_ASSERT_TRUE(count == 2);
+
+ etchmap_del(map,key1);
+ etchmap_del(map,key2);
+
+ count = etchmap_count(map);
+ CU_ASSERT_TRUE(count == 0);
+
+ key1 = new_int32(2);
+ etchmap_add(map, key1, content1);
+ key1->value = 3;
+ etchmap_add(map, key1, content2);
+
+ count = etchmap_count(map);
+ CU_ASSERT_TRUE(count == 1);
+}
+
+/**
+ * test_new_etch_hashtable
+ * unit test for new_etch_hashtable
+ */
+//void test_new_etch_hashtable(void)
+//{
+// /* new_etch_hashtable does not have meat in it */
+// etch_hashtable* eht = new_etch_hashtable();
+// CU_ASSERT_PTR_NOT_NULL_FATAL(eht);
+// CU_ASSERT_PTR_NULL(eht->realtable);
+// CU_ASSERT_PTR_NULL(((etch_object*)eht)->vtab);
+// etch_free(eht);
+//}
+
+/**
+ * test_destroy_hashtable
+ * unit test for destroy_hashtable
+ */
+//void test_destroy_hashtable(void)
+//{
+// wchar_t *key = NULL, *val = NULL;
+//
+// /* try to delete key and value but nothing in it */
+// etch_hashtable* eht = new_etch_hashtable();
+// destroy_hashtable(eht, TRUE, TRUE);
+//
+// /* negative test with NULL hash table */
+// destroy_hashtable(NULL, TRUE, TRUE);
+//
+// /* TODO: add more cases to verify key/value deletion */
+//}
+
+/**
+ * test_jenkins_insert
+ * unit test for jenkins_insert
+ */
+static void test_jenkins_insert(void)
+{
+ int keylen = 0, vallen=0;
+ wchar_t key[100];
+
+ etch_hashtable* eht = new_hashtable(16);
+
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
+
+ etch_snwprintf(key, 100, L"key%d", 0);
+ keylen = (int)wcslen(key);
+ vallen = 3;
+ jenkins_insert(eht->realtable, key, (keylen+1)*sizeof(wchar_t), L"val", (vallen+1)*sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
+ /* insert the same key value pair */
+ jenkins_insert(eht->realtable, key, (keylen+1)*sizeof(wchar_t), L"val", (vallen+1)*sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
+
+ /* insert the same key but different value */
+ jenkins_insert(eht->realtable, key, (keylen+1)*sizeof(wchar_t), L"va1", (vallen+1)*sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
+
+ /* insert different key but the same value */
+ etch_snwprintf(key, 100, L"key%d", 1);
+ jenkins_insert(eht->realtable, key, (keylen+1)*sizeof(wchar_t), L"val", (vallen+1)*sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
+
+ /* insert different key value pair */
+ etch_snwprintf(key, 100, L"key%d", 2);
+ jenkins_insert(eht->realtable, key, (keylen+1)*sizeof(wchar_t), L"va1", (vallen+1)*sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 3);
+
+ /* insert NULL key and value */
+ jenkins_insert(eht->realtable, NULL, 0, NULL, 0, 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 3);
+
+ etch_object_destroy(eht);
+}
+
+/**
+ * test_jenkins_find
+ * unit test for jenkins_find
+ */
+static void test_jenkins_find(void)
+{
+ int result = 0, keylen = 0, vallen = 0;
+ wchar_t* key1 = 0;
+ wchar_t* key2 = 0;
+ wchar_t* val1 = 0;
+ wchar_t* val2 = 0;
+ etch_hashitem* outentry;
+ etch_hashtable* eht = 0;
+
+ eht = new_hashtable(32);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+
+ outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
+ outentry->key = malloc(sizeof(wchar_t) * 100);
+ outentry->value = malloc(sizeof(wchar_t) * 100);
+
+ key1 = malloc(sizeof(wchar_t) * 100);
+ key2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(key1, 100, L"key%d", 1);
+ etch_snwprintf(key2, 100, L"key%d", 2);
+ keylen = (int)wcslen(key1);
+
+ val1 = malloc(sizeof(wchar_t) * 100);
+ val2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(val1, 100, L"val%d", 1);
+ etch_snwprintf(val2, 100, L"val%d", 2);
+ vallen = (int)wcslen(val1);
+
+ jenkins_insert(eht->realtable, key1, (keylen + 1)*sizeof(wchar_t), val1, (vallen + 1)*sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
+
+ /* get first, first key = first key */
+ result = jenkins_find(eht->realtable, key2, keylen*2+2, 0, &outentry);
+ CU_ASSERT_EQUAL(result, -1);
+
+ jenkins_insert(eht->realtable, key2, keylen*2+2, val2, vallen*2+2, 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
+
+ /* get first, first key = first key */
+ result = jenkins_find(eht->realtable, key2, keylen*2+2, 0, &outentry);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key2, outentry->key);
+
+ etch_object_destroy(eht);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_jenkins_findh
+ * unit test for jenkins_findh
+ */
+static void test_jenkins_findh(void)
+{
+ int result = 0, keylen = 0, vallen = 0, hash = 0, bytes_allocated = 0;
+ wchar_t* key1 = 0;
+ wchar_t* key2 = 0;
+ wchar_t* val1 = 0;
+ wchar_t* val2 = 0;
+ etch_hashitem* outentry;
+
+ etch_hashtable* eht = new_hashtable(32);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+
+ outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
+ outentry->key = malloc(sizeof(wchar_t) * 100);
+ outentry->value = malloc(sizeof(wchar_t) * 100);
+
+ key1 = malloc(sizeof(wchar_t) * 100);
+ key2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(key1, 100, L"key%d", 1);
+ etch_snwprintf(key2, 100, L"key%d", 2);
+ keylen = (int)wcslen(key1);
+
+ val1 = malloc(sizeof(wchar_t) * 100);
+ val2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(val1, 100, L"val%d", 1);
+ etch_snwprintf(val2, 100, L"val%d", 2);
+ vallen = (int)wcslen(val1);
+
+ jenkins_insert(eht->realtable, key1, (keylen + 1) * sizeof(wchar_t), val1, (vallen + 1) * sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
+ jenkins_insert(eht->realtable, key2, (keylen + 1) * sizeof(wchar_t), val2, (vallen + 1) * sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
+
+ /* Find the second key */
+ result = jenkins_find(eht->realtable, key2, (keylen + 1) * sizeof(wchar_t), 0, &outentry);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key2, outentry->key);
+
+ etch_object_destroy(eht);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * etch_testobject
+ * test object for test_jenkins_inserth
+ */
+typedef struct etch_testobject
+{
+ etch_object object;
+
+ char* hashvalue;
+
+} etch_testobject;
+
+
+
+/**
+ * test_inserth_get_hashkey
+ * object hashkey computation virtual for test_jenkins_inserth
+ * attempts to compute and set hashkey in object, returning hashkey so set, or zero.
+ */
+static int test_inserth_get_hashkey(etch_object* obj)
+{
+ char* hashitem = ((etch_testobject*)obj)->hashvalue;
+ if (NULL == hashitem) return 0;
+ return obj->hashkey = jenkins_hashx(hashitem, (int) strlen(hashitem), 0);
+}
+
+
+/**
+ * new_testobject
+ * construct test object for test_jenkins_inserth
+ * illustrates that constructor should set the get_hashkey virtual.
+ */
+static etch_testobject* new_testobject(char* hashsource)
+{
+ etch_testobject* newobj = (etch_testobject*) new_object
+ (sizeof(etch_testobject), ETCHTYPEB_NONE, CLASSID_NONE);
+
+ newobj->hashvalue = hashsource;
+
+ ((etch_object*)newobj)->get_hashkey = test_inserth_get_hashkey;
+
+ ((etch_object*)newobj)->get_hashkey((etch_object*)newobj);
+
+ return newobj;
+}
+
+
+
+/**
+ * test_inserth_freehandler
+ * hashtable content memory management callback for test_jenkins_inserth
+ */
+static int test_inserth_freehandler(etch_object* key, etch_object* val)
+{
+ etch_object_destroy(key);
+ etch_object_destroy(val);
+ return 0;
+}
+
+
+/**
+ * test_jenkins_inserth
+ * unit test for jenkins_inserth
+ * this test was written before the etch etch_object* included a get_hashkey virtual
+ */
+static void test_jenkins_inserth(void)
+{
+ int result = 0;
+ unsigned hash1 = 0, hash2 = 0;
+ etch_hashitem outentry, *outitem = &outentry;
+ char *s1 = "abracadabra", *s2 = "gilgamesh";
+ etch_testobject *key1, *key2;
+ etch_object *val1, *val2;
+ etch_hashtable* eht;
+ memset(outitem, 0, sizeof(etch_hashitem));
+
+ key1 = new_testobject(s1);
+ key2 = new_testobject(s2);
+
+ val1 = new_object(sizeof(etch_object), ETCHTYPEB_NONE, CLASSID_NONE);
+ val2 = new_object(sizeof(etch_object), ETCHTYPEB_NONE, CLASSID_NONE);
+
+ eht = new_hashtable(32);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(eht);
+
+ /* set hook to free all hashtable content when we destroy hashtable */
+ eht->freehook = test_inserth_freehandler;
+
+ CU_ASSERT_NOT_EQUAL_FATAL(((etch_object*)key1)->hashkey, 0); /* ensure test object ctor set hashkey */
+ CU_ASSERT_NOT_EQUAL_FATAL(((etch_object*)key2)->hashkey, 0);
+ /* recompute expected hashkeys and compare to effective hashkeys */
+ hash1 = jenkins_hashx(s1, (int) strlen(s1), 0);
+ hash2 = jenkins_hashx(s2, (int) strlen(s2), 0);
+ CU_ASSERT_EQUAL(hash1,((etch_object*)key1)->hashkey);
+ CU_ASSERT_EQUAL(hash2,((etch_object*)key2)->hashkey);
+
+ /* insert pairs to map using the pre-computed hashkeys */
+ result = jenkins_inserth(eht->realtable, key1, val1, NULL, NULL);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
+
+ result = jenkins_inserth(eht->realtable, key2, val2, NULL, NULL);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
+
+ /* look them up using expected hashkeys */
+ result = jenkins_findh(eht->realtable, hash1, 0, &outitem);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key1, outentry.key);
+
+ result = jenkins_findh(eht->realtable, hash2, 0, &outitem);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key2, outentry.key);
+
+ etch_object_destroy(eht);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * default_get_hashkey
+ * object hashkey computation virtual for test_default_hashkey
+ * attempts to compute and set a hashkey in object,
+ * for use when no dedicated override is supplied.
+ */
+static int default_get_hashkey(etch_object* obj)
+{
+ void* hashitem = obj; /* we use the object address as hash source */
+ return obj->hashkey = jenkins_hashx((char*)&hashitem, sizeof(void*), 0);
+}
+
+
+/**
+ * new_default_object
+ * test object constructor for test_default_hashkey
+ * sets a default hashkey function and invokes it
+ */
+static etch_testobject* new_default_object()
+{
+ etch_testobject* newobj = (etch_testobject*) new_object
+ (sizeof(etch_testobject), ETCHTYPEB_NONE, CLASSID_NONE);
+
+ ((etch_object*)newobj)->get_hashkey = default_get_hashkey;
+
+ ((etch_object*)newobj)->get_hashkey((etch_object*)newobj);
+
+ return newobj;
+}
+
+
+/**
+ * test_default_hashkey
+ * unit test for hashing using the default hashkey function
+ * this test was written before the etch etch_object* included a get_hashkey virtual
+ */
+static void test_default_hashkey(void)
+{
+ int result = 0;
+ unsigned hash1 = 0, hash2 = 0;
+ etch_hashitem outentry, *outitem = &outentry;
+ etch_testobject *key1, *key2, *val1, *val2;
+ etch_hashtable* eht;
+ memset(outitem, 0, sizeof(etch_hashitem));
+
+ key1 = new_default_object();
+ key2 = new_default_object();
+ CU_ASSERT_NOT_EQUAL_FATAL(((etch_object*)key1)->hashkey, 0); /* ensure test object ctor set hashkey */
+ CU_ASSERT_NOT_EQUAL_FATAL(((etch_object*)key2)->hashkey, 0);
+ val1 = new_default_object();
+ val2 = new_default_object();
+ eht = new_hashtable(32);
+ /* set hook to free all hashtable content when we destroy hashtable */
+ eht->freehook = test_inserth_freehandler;
+
+ /* insert pairs to map using the pre-computed hashkeys */
+ result = jenkins_inserth(eht->realtable, key1, val1, NULL, NULL);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
+
+ result = jenkins_inserth(eht->realtable, key2, val2, NULL, NULL);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
+
+ /* look them up using effective hashkeys */
+ result = jenkins_findh(eht->realtable, ((etch_object*)key1)->hashkey, 0, &outitem);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key1, outentry.key);
+
+ result = jenkins_findh(eht->realtable, ((etch_object*)key2)->hashkey, 0, &outitem);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key2, outentry.key);
+
+ /* compute expected hashkeys from addresses of key objects */
+ hash1 = jenkins_hashx((char*)&key1, sizeof(void*), 0);
+ hash2 = jenkins_hashx((char*)&key2, sizeof(void*), 0);
+
+ /* look them up using computed hashkeys */
+ result = jenkins_findh(eht->realtable, hash1, 0, &outitem);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key1, outentry.key);
+
+ result = jenkins_findh(eht->realtable, hash2, 0, &outitem);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key2, outentry.key);
+
+ etch_object_destroy(eht);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_jenkins_first
+ * unit test for jenkins_first
+ */
+static void test_jenkins_first(void)
+{
+ int result = 0, keylen = 0, vallen = 0;
+ wchar_t* key1 = 0;
+ wchar_t* key2 = 0;
+ wchar_t* val1 = 0;
+ wchar_t* val2 = 0;
+ etch_hashitem* outentry;
+ int ptrEqual;
+
+ etch_hashtable* eht = new_hashtable(32);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+
+ outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
+ outentry->key = malloc(sizeof(wchar_t) * 100);
+ outentry->value = malloc(sizeof(wchar_t) * 100);
+
+ key1 = malloc(sizeof(wchar_t) * 100);
+ key2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(key1, 100, L"key%d", 1);
+ etch_snwprintf(key2, 100, L"key%d", 2);
+ keylen = (int)wcslen(key1);
+
+ val1 = malloc(sizeof(wchar_t) * 100);
+ val2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(val1, 100, L"val%d", 1);
+ etch_snwprintf(val2, 100, L"val%d", 2);
+ vallen = (int)wcslen(val1);
+
+ jenkins_insert(eht->realtable, key1, (keylen + 1) * sizeof(wchar_t), val1, (vallen + 1) * sizeof(wchar_t), 0, 0);
+ jenkins_insert(eht->realtable, key2, (keylen + 1) * sizeof(wchar_t), val2, (vallen + 1) * sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
+
+ /* get first, first key = first key */
+ result = jenkins_first(eht->realtable, 0, &outentry);
+ CU_ASSERT_EQUAL(result, 0);
+ printf("jenkins_first: %ls=%ls\n", key1, (wchar_t*)outentry->key);
+ ptrEqual = key1 == (wchar_t*)outentry->key ||
+ key2 == (wchar_t*)outentry->key;
+ CU_ASSERT_TRUE(ptrEqual);
+
+ /* next is key2 */
+ result = jenkins_next(eht->realtable, 0, &outentry);
+ CU_ASSERT_EQUAL(result, 0);
+ ptrEqual = key1 == (wchar_t*)outentry->key ||
+ key2 == (wchar_t*)outentry->key;
+ CU_ASSERT_TRUE(ptrEqual);
+
+ etch_object_destroy(eht);
+}
+
+
+/**
+ * test_jenkins_next
+ * unit test for jenkins_next
+ */
+static void test_jenkins_next(void)
+{
+ int result = 0, keylen = 0, vallen = 0;
+ wchar_t* key1 = 0;
+ wchar_t* key2 = 0;
+ wchar_t* key3 = 0;
+ wchar_t* val1 = 0;
+ wchar_t* val2 = 0;
+ wchar_t* val3 = 0;
+ etch_hashitem* outentry;
+
+ etch_hashtable* eht = new_hashtable(32);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+
+ outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
+ outentry->key = malloc(sizeof(wchar_t) * 100);
+ outentry->value = malloc(sizeof(wchar_t) * 100);
+
+ key1 = malloc(sizeof(wchar_t) * 100);
+ key2 = malloc(sizeof(wchar_t) * 100);
+ key3 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(key1, 100, L"key%d", 1);
+ etch_snwprintf(key2, 100, L"key%d", 2);
+ etch_snwprintf(key3, 100, L"key%d", 3);
+ keylen = (int)wcslen(key1);
+
+ val1 = malloc(sizeof(wchar_t) * 100);
+ val2 = malloc(sizeof(wchar_t) * 100);
+ val3 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(val1, 100, L"val%d", 1);
+ etch_snwprintf(val2, 100, L"val%d", 2);
+ etch_snwprintf(val3, 100, L"val%d", 3);
+ vallen = (int)wcslen(val1);
+
+ jenkins_insert(eht->realtable, key1, (keylen+1)*sizeof(wchar_t), val1, (vallen+1)*sizeof(wchar_t), 0, 0);
+ jenkins_insert(eht->realtable, key2, (keylen+1)*sizeof(wchar_t), val2, (vallen+1)*sizeof(wchar_t), 0, 0);
+ jenkins_insert(eht->realtable, key3, (keylen+1)*sizeof(wchar_t), val3, (vallen+1)*sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 3);
+
+ result = jenkins_first(eht->realtable, 0, &outentry);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = jenkins_next(eht->realtable, 0, &outentry);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = jenkins_next(eht->realtable, 0, &outentry);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = jenkins_next(eht->realtable, 0, &outentry);
+ CU_ASSERT_EQUAL(result, -1);
+
+ etch_object_destroy(eht);
+}
+
+
+/**
+ * test_jenkins_current
+ * unit test for jenkins_current
+ */
+static void test_jenkins_current(void)
+{
+ int result = 0, keylen = 0, vallen = 0;
+ wchar_t* key1 = 0;
+ wchar_t* key2 = 0;
+ wchar_t* val1 = 0;
+ wchar_t* val2 = 0;
+ etch_hashitem* outentry;
+
+ etch_hashtable* eht = new_hashtable(32);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+
+ /* get current, no key */
+ outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
+ outentry->key = malloc(sizeof(wchar_t) * 100);
+ outentry->value = malloc(sizeof(wchar_t) * 100);
+ result = jenkins_current(eht->realtable, 0, &outentry);
+ CU_ASSERT_EQUAL(result, -1);
+
+ key1 = malloc(sizeof(wchar_t) * 100);
+ key2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(key1, 100, L"key%d", 1);
+ etch_snwprintf(key2, 100, L"key%d", 2);
+ keylen = (int)wcslen(key1);
+
+ val1 = malloc(sizeof(wchar_t) * 100);
+ val2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(val1, 100, L"val%d", 1);
+ etch_snwprintf(val2, 100, L"val%d", 2);
+ vallen = (int)wcslen(val1);
+
+ jenkins_insert(eht->realtable, key1, (keylen+1)*sizeof(wchar_t), val1, (vallen+1)*sizeof(wchar_t), 0, 0);
+ jenkins_insert(eht->realtable, key2, (keylen+1)*sizeof(wchar_t), val2, (vallen+1)*sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
+
+ /* get current, current key = key2 */
+ result = jenkins_current(eht->realtable, 0, &outentry);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key2, outentry->key);
+
+ /* remove second entry */
+ result = jenkins_remove(eht->realtable, key2, (keylen+1)*sizeof(wchar_t), 0, &outentry);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key2, outentry->key);
+
+ /* get current, current key = key1 */
+ result = jenkins_current(eht->realtable, 0, &outentry);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(key1, outentry->key);
+
+ etch_object_destroy(eht);
+}
+
+/**
+ * test_jenkins_remove
+ * unit test for jenkins_remove
+ */
+static void test_jenkins_remove(void)
+{
+ int count = 0, keylen = 0, vallen = 0;
+ wchar_t* key1 = 0;
+ wchar_t* key2 = 0;
+ wchar_t* val1 = 0;
+ //wchar_t* val2 = 0;
+ etch_hashitem* outentry;
+
+ etch_hashtable* eht = new_hashtable(32);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+
+ key1 = malloc(sizeof(wchar_t) * 100);
+ key2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(key1, 100, L"key%d", 1);
+ etch_snwprintf(key2, 100, L"key%d", 2);
+ keylen = (int)wcslen(key1);
+
+ val1 = malloc(sizeof(wchar_t) * 100);
+ //val2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(val1, 100, L"val%d", 1);
+ //etch_snwprintf(val2, 100, L"val%d", 2);
+ vallen = (int)wcslen(val1);
+
+ outentry = (etch_hashitem*)malloc(sizeof(etch_hashitem));
+ outentry->key = malloc(sizeof(wchar_t) * 100);
+ outentry->value = malloc(sizeof(wchar_t) * 100);
+
+ jenkins_insert(eht->realtable, key1, (keylen+1)*sizeof(wchar_t), val1, (vallen+1)*sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
+
+ /* remove wrong entry */
+ count = jenkins_remove(eht->realtable, key2, (keylen+1)*sizeof(wchar_t), 0, &outentry);
+ CU_ASSERT_EQUAL(count, -1);
+
+ /* remove the entry */
+ count = jenkins_remove(eht->realtable, key1,(keylen+1)*sizeof(wchar_t), 0, &outentry);
+ CU_ASSERT_EQUAL(count, 0);
+ CU_ASSERT_PTR_EQUAL(key1, outentry->key);
+
+ etch_object_destroy(eht);
+}
+
+
+/**
+ * test_jenkins_clear
+ * unit test for jenkins_clear
+ */
+static void test_jenkins_clear(void)
+{
+ int count = 0, keylen = 0, vallen = 0;
+ wchar_t* key1 = 0;
+ wchar_t* key2 = 0;
+ wchar_t* val1 = 0;
+ wchar_t* val2 = 0;
+
+ etch_hashtable* eht = new_hashtable(32);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+
+ key1 = malloc(sizeof(wchar_t) * 100);
+ key2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(key1, 100, L"key%d", 1);
+ etch_snwprintf(key2, 100, L"key%d", 2);
+ keylen = (int)wcslen(key1);
+
+ val1 = malloc(sizeof(wchar_t) * 100);
+ val2 = malloc(sizeof(wchar_t) * 100);
+ etch_snwprintf(val1, 100, L"val%d", 1);
+ etch_snwprintf(val2, 100, L"val%d", 2);
+ vallen = (int)wcslen(val1);
+
+ jenkins_insert(eht->realtable, key1, (keylen+1)*sizeof(wchar_t), val1, (vallen+1)*sizeof(wchar_t), 0, 0);
+ jenkins_insert(eht->realtable, key2, (keylen+1)*sizeof(wchar_t), val2, (vallen+1)*sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 2);
+ count = jenkins_clear(eht->realtable, TRUE, TRUE, 0, 0);
+ /* clear 2 key/val pair */
+ CU_ASSERT_EQUAL(count, 2);
+ CU_ASSERT_PTR_NOT_NULL(eht->realtable);
+
+ etch_object_destroy(eht);
+}
+
+
+/**
+ * test_jenkins_count
+ * unit test for jenkins_count
+ */
+static void test_jenkins_count(void)
+{
+ etch_hashtable* eht = new_hashtable(32);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
+ jenkins_insert(eht->realtable, L"key", 4 * sizeof(wchar_t), L"val", 4 * sizeof(wchar_t), 0, 0);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 1);
+ etch_object_destroy(eht);
+}
+
+/**
+ * test_jenkins_size
+ * unit test for jenkins_size
+ */
+static void test_jenkins_size(void)
+{
+ int i = 0;
+ int keylen = 0;
+ wchar_t key[100];
+
+ etch_hashtable* eht = new_hashtable(16);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
+ for (i=0; i<17; i++)
+ {
+ etch_snwprintf(key, 100, L"key%d", i);
+ keylen = (int)wcslen(key);
+ jenkins_insert(eht->realtable, key, (keylen + 1) * sizeof(wchar_t), L"val", 4 * sizeof(wchar_t), 0, 0);
+ }
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 32);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 17);
+ etch_object_destroy(eht);
+}
+
+/**
+ * test_jenkins_stats
+ * unit test for jenkins_stats
+ */
+static void test_jenkins_stats(void)
+{
+
+}
+
+/**
+ * test_jenkins_hash
+ * unit test for jenkins_hash
+ */
+//void test_jenkins_hash(void)
+//{
+// int hash = 0;
+//
+// etch_hashtable* eht = new_etch_hashtable();
+// CU_ASSERT_PTR_NOT_NULL(eht);
+// CU_ASSERT_PTR_NULL(eht->realtable);
+// /* realtable is NULL */
+// hash = jenkins_hash(eht->realtable, "key", 3, 0, 0, 0);
+// CU_ASSERT_EQUAL(hash, 0);
+// destroy_hashtable(eht, TRUE, TRUE);
+//
+// eht = new_hashtable(16);
+// CU_ASSERT_PTR_NOT_NULL(eht);
+// CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
+// CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
+// /* key is NULL */
+// hash = jenkins_hash(eht->realtable, 0, 0, 0, 0, 0);
+// CU_ASSERT_EQUAL(hash, 0);
+// /* key length is 0 */
+// hash = jenkins_hash(eht->realtable, "key", 0, 0, 0, 0);
+// CU_ASSERT_EQUAL(hash, 0);
+// /* key length is MAX_ETCHHASHKEY + 1 */
+// hash = jenkins_hash(eht->realtable, "key", MAX_ETCHHASHKEY+1, 0, 0, 0);
+// CU_ASSERT_EQUAL(hash, 0);
+// /* get a good hash */
+// hash = jenkins_hash(eht->realtable, "key", 3, 0, 0, 0);
+// CU_ASSERT_NOT_EQUAL(hash, 0);
+// /* hash should be the same is input arguments are the same */
+// CU_ASSERT_EQUAL(hash, jenkins_hash(eht->realtable, "key", 3, 0, 0, 0));
+// /* hash should be different if input arguments are different */
+// CU_ASSERT_NOT_EQUAL(hash, jenkins_hash(eht->realtable, "kee", 3, 0, 0, 0));
+etch_object_destroy(eht);
+//}
+
+
+/**
+ * test_intkey
+ * test using a 32 bit int as a key
+ */
+#pragma warning (disable:4996)
+static void test_intkey(void)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* myentry = &hashbucket;
+ char* test_string = "it works";
+ unsigned int key = 12345, key2 = 11111, key3 = 11111;
+ int value_len = (int)strlen(test_string)+1; /* string length of value */
+ struct i_hashtable* vtab = NULL;
+ etch_hashtable* ht = new_hashtable(4);
+
+ /* our hash key is a 4-byte item, which we will use to contain the
+ value of a 32 bit int.
+ */
+ char* pkey = malloc(sizeof(int));
+
+ /* the string pval is our hash value */
+ char* pval = malloc(value_len);
+ strcpy(pval, test_string);
+
+ /* copy the int into our hash key.
+ */
+ memcpy(pkey, &key, sizeof(int));
+ vtab = (struct i_hashtable*)((etch_object*)ht)->vtab;
+ CU_ASSERT_EQUAL(vtab->insert(ht->realtable, pkey, sizeof(int), pval, value_len, 0, 0), 0);
+ CU_ASSERT_EQUAL(vtab->find (ht->realtable, pkey, sizeof(int), NULL, &myentry), 0);
+ CU_ASSERT_EQUAL(vtab->find (ht->realtable, &key, sizeof(int), NULL, &myentry), 0);
+
+ /* test two pointers to the same value.
+ */
+ CU_ASSERT_EQUAL(vtab->insert(ht->realtable, &key2, sizeof(int), pval, value_len, 0, 0), 0);
+ CU_ASSERT_EQUAL(vtab->find (ht->realtable, &key3, sizeof(int), NULL, &myentry), 0);
+
+ destroy_hashtable(ht, FALSE, FALSE);
+ free(pkey);
+ free(pval);
+}
+
+
+/**
+ * test_jenkins_destroy
+ * unit test for jenkins_destroy
+ */
+//void test_jenkins_destroy(void)
+//{
+// int result = 0;
+//
+// etch_hashtable* eht = new_etch_hashtable();
+// CU_ASSERT_PTR_NOT_NULL(eht);
+// CU_ASSERT_PTR_NULL(eht->realtable);
+// /* realtable is NULL */
+// result = jenkins_destroy(eht->realtable, 0, 0);
+// CU_ASSERT_EQUAL(result, -1);
+//
+// eht = new_hashtable(16);
+// CU_ASSERT_PTR_NOT_NULL(eht);
+// CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
+// CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
+// /* realtable is not NULL */
+// result = jenkins_destroy(eht->realtable, 0, 0);
+// CU_ASSERT_EQUAL(result, 0);
+//}
+
+/**
+ * test_jenkins_create
+ * unit test for jenkins_create
+ */
+//void test_jenkins_create(void)
+//{
+// int result = 0;
+//
+// etch_hashtable* eht = new_etch_hashtable();
+// CU_ASSERT_PTR_NOT_NULL(eht);
+// CU_ASSERT_PTR_NULL(eht->realtable);
+// /* realtable is NULL */
+// result = jenkins_create(16, 0, eht->realtable);
+// CU_ASSERT_EQUAL(result, -1);
+// destroy_hashtable(eht, FALSE, FALSE);
+//
+// eht = new_hashtable(16);
+// CU_ASSERT_PTR_NOT_NULL(eht);
+// CU_ASSERT_PTR_NOT_NULL(eht->realtable);
+// /* realtable is NULL */
+// result = jenkins_create(16, 0, eht->realtable);
+// CU_ASSERT_EQUAL(result, 0);
+// CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
+// CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
+// destroy_hashtable(eht, FALSE, FALSE);
+//}
+
+/**
+ * test_ctor_jenkins_hashtable
+ * unit test for ctor_jenkins_hashtable
+ */
+static void test_ctor_jenkins_hashtable(void)
+{
+ etch_hashtable* eht = ctor_jenkins_hashtable(16);
+ CU_ASSERT_PTR_NOT_NULL(eht);
+ CU_ASSERT_PTR_NOT_NULL(eht->realtable);
+ CU_ASSERT_PTR_NOT_NULL(((etch_object*)eht)->vtab);
+ CU_ASSERT_EQUAL(jenkins_size(eht->realtable, 0, 0), 16);
+ CU_ASSERT_EQUAL(jenkins_count(eht->realtable, 0, 0), 0);
+
+ destroy_hashtable(eht, FALSE, FALSE);
+}
+
+
+#pragma warning (disable:4996)
+
+/*
+ * test_key_pointer: test using a memory address as a hash key.
+ * return 1 if OK, zero if failed.
+ */
+static int test_key_pointer(etch_hashtable* ht)
+{
+ etch_hashitem hashbucket;
+ etch_hashitem* myentry = &hashbucket;
+ char* test_string = "it works";
+ const int value_len = (int)strlen(test_string)+1; /* string length of value */
+ const int key_len = sizeof(char*);
+
+ /* our hash key is a 4-byte item, which we will use to contain the
+ value of a memory address.
+ */
+ char* pkey = malloc(key_len);
+
+ /* the value of the pval memory address is our hash key.
+ * the string pval is our hash value. Of course, our hash table stores pointers,
+ * not values, so the key is &pval, and the value is pval.
+ */
+ char* pval = malloc(value_len);
+ strcpy(pval, test_string);
+
+ /* copy the memory address into our hash key.
+ */
+ memcpy(pkey, &pval, sizeof(char*));
+
+ CU_ASSERT_EQUAL(((struct i_hashtable*)((etch_object*)ht)->vtab)->insert(ht->realtable, pkey, key_len, pval, value_len, 0, 0), 0);
+
+ CU_ASSERT_EQUAL(((struct i_hashtable*)((etch_object*)ht)->vtab)->find(ht->realtable, pkey, key_len, NULL, &myentry), 0);
+
+ CU_ASSERT_PTR_NOT_NULL(myentry->value);
+
+ /* This code demonstrates what we've done here. The key is the value of a
+ * char*, and the value is that same char*. So, indirectly dereferencing
+ * the key must elicit the same result as dereferencing the value.
+ */
+ CU_ASSERT_EQUAL(memcmp(myentry->key, &myentry->value, sizeof(char*)), 0);
+
+ CU_ASSERT_EQUAL(strcmp(*(char**)myentry->key, (char*)myentry->value), 0);
+
+ return 1;
+}
+
+/**
+ * test_all
+ * unit test for various functions strung together
+ */
+static void test_combo(void)
+{
+ char buf[MAXLINELEN];
+ char* pkey; void* pdata;
+ int n, keylen, myincount=0, myhashcount=0, myfreecount=0;
+ etch_hashtable* myhash;
+ etch_hashitem hashbucket;
+ etch_hashitem* myentry = &hashbucket;
+ unsigned hashed;
+ char c = 0, *p;
+ FILE* f = 0;
+ p = "abracadabra"; n = (int)strlen(p); // Test the hash function
+ hashed = etchhash(p, n, 0);
+
+ f = fopen(TESTDATAPATH,"r");
+ CU_ASSERT_PTR_NOT_NULL_FATAL(f);
+
+ myhash = new_hashtable(INITIAL_TABLE_SIZE); /* create hash table */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(myhash);
+
+ ((struct i_hashtable*)((etch_object*)myhash)->vtab)->stats(myhash->realtable, 0, 0); /* show table stats */
+ n = ((struct i_hashtable*)((etch_object*)myhash)->vtab)->size(myhash->realtable, 0, 0);
+ CU_ASSERT_EQUAL(n, INITIAL_TABLE_SIZE);
+
+ while (fgets((char*)buf, MAXLINELEN, f)) /* read the testdata file line by line */
+ {
+ unsigned char* p = buf; while(*p++) if (*p <= 0x0d) *p = 0; /* strip crlf */
+ keylen = (int)strlen(buf); /* we use null termed key, but it is not necessary */
+ pkey = malloc(keylen+1); strcpy(pkey, buf);
+ pdata = malloc(DATASIZE); /* note that user allocates memory for key and data */
+ strcpy(pdata, DATAVALUE); /* hashtable does not make copies of key or of data */
+ /* insert the new hashtable entry */
+ if (0 == ((struct i_hashtable*)((etch_object*)myhash)->vtab)->insert(myhash->realtable, pkey, keylen, pdata, DATASIZE, 0, 0))
+ { myincount++; /* point at current entry in hashtable */
+ ((struct i_hashtable*)((etch_object*)myhash)->vtab)->current(myhash->realtable, 0, &myentry);
+ CU_ASSERT_PTR_EQUAL(myentry->key, pkey); /* ensure items just inserted are there */
+ CU_ASSERT_PTR_EQUAL(myentry->value, pdata);
+ }
+ else /* insert failure - probably duplicate */
+ { free(pkey);
+ free(pdata);
+ }
+ }
+
+ fclose(f); f = 0;
+ ((struct i_hashtable*)((etch_object*)myhash)->vtab)->stats(myhash->realtable, 0, 0); /* show table stats */
+
+ pkey = "banana"; /* test a hashtable lookup */
+
+ CU_ASSERT_EQUAL(((struct i_hashtable*)((etch_object*)myhash)->vtab)->find(myhash->realtable, pkey, (int)strlen(pkey), NULL, &myentry), 0);
+ CU_ASSERT_EQUAL(strcmp(pkey, myentry->key), 0);
+
+ CU_ASSERT_EQUAL(((struct i_hashtable*)((etch_object*)myhash)->vtab)->first(myhash->realtable, NULL, &myentry), 0);
+ myhashcount = 1;
+
+ while(0 == ((struct i_hashtable*)((etch_object*)myhash)->vtab)->next(myhash->realtable, NULL, &myentry))
+ {
+ myhashcount++;
+ }
+
+ /* test using pointer as key, adding 0 or 1 to myhashcount */
+ myhashcount += test_key_pointer(myhash);
+
+ /* destroy hash table and all its contents (free key and data memory) */
+ myfreecount = ((struct i_hashtable*)((etch_object*)myhash)->vtab)->clear(myhash->realtable, TRUE, TRUE, 0, 0);
+
+ CU_ASSERT_EQUAL(myfreecount, myhashcount);
+}
+
+
+/**
+ * _tmain
+ * unit test entry point
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_hashtable_suite()
+{
+ CU_pSuite ps = CU_add_suite("suite_etchhash", init_suite, clean_suite);
+
+ CU_add_test(ps, "new_hashtable", test_new_hashtable);
+ //CU_add_test(ps, "new_etch_hashtable", test_new_etch_hashtable);
+ //CU_add_test(ps, "destroy_hashtable", test_destroy_hashtable);
+ //CU_add_test(ps, "jenkins_hash", test_jenkins_hash);
+ CU_add_test(ps, "jenkins_find", test_jenkins_find);
+ CU_add_test(ps, "jenkins_findh", test_jenkins_findh);
+ CU_add_test(ps, "jenkins default key", test_default_hashkey);
+ CU_add_test(ps, "jenkins_first", test_jenkins_first);
+ CU_add_test(ps, "jenkins_next", test_jenkins_next);
+ CU_add_test(ps, "jenkins_current", test_jenkins_current);
+ CU_add_test(ps, "jenkins_remove", test_jenkins_remove);
+ CU_add_test(ps, "jenkins_clear", test_jenkins_clear);
+ CU_add_test(ps, "jenkins_count", test_jenkins_count);
+ CU_add_test(ps, "jenkins_size", test_jenkins_size);
+ CU_add_test(ps, "jenkins_insert", test_jenkins_insert);
+ CU_add_test(ps, "jenkins_inserth", test_jenkins_inserth);
+ CU_add_test(ps, "jenkins_stats", test_jenkins_stats);
+ //CU_add_test(ps, "jenkins_destroy", test_jenkins_destroy);
+ //CU_add_test(ps, "jenkins_create", test_jenkins_create);
+ CU_add_test(ps, "ctor_jenkins_hashtable", test_ctor_jenkins_hashtable);
+ CU_add_test(ps, "test integer key", test_intkey);
+ CU_add_test(ps, "test combinations", test_combo);
+ CU_add_test(ps, "test map", test_map);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/common/test_iterator.c b/binding-c/runtime/c/src/test/common/test_iterator.c
new file mode 100644
index 0000000..e85c151
--- /dev/null
+++ b/binding-c/runtime/c/src/test/common/test_iterator.c
@@ -0,0 +1,335 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_iterator.c -- test etch_iterator over i_iterable classes
+ */
+#include "etch_runtime.h"
+#include "etch_arraylist.h"
+#include "etch_hash.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+#include <wchar.h>
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+static etch_arraylist* testlist;
+static etch_hashtable* testhash;
+
+/*
+ * load_listdata_int()
+ * load testlist array with some etch_int32 objects
+ */
+static int load_listdata_int()
+{
+ int i = 0, numitems = 4;
+ etch_int32* newobj = NULL;
+ int ints[4] = { 1, 2, 3, 4 };
+
+ for(; i < numitems; i++)
+ {
+ newobj = new_int32(ints[i]);
+ etch_arraylist_add(testlist, newobj);
+ }
+
+ return numitems;
+}
+
+
+/*
+ * new_listdata()
+ * create testlist array and load it up with data objects
+ */
+static int new_listdata(const int datatype)
+{
+ int count = 0;
+ testlist = new_etch_arraylist(0,0);
+ testlist->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
+ count = load_listdata_int();
+ return count;
+}
+
+
+/*
+ * destroy_listdata()
+ * destroy testlist array and content
+ */
+static void destroy_listdata()
+{
+ etch_arraylist_destroy(testlist, TRUE);
+}
+
+
+/*
+ * load_hashdata_string()
+ * load testhash hashtable with some etch_string objects
+ */
+static int load_hashdata_string()
+{
+ int i = 0, numitems = 4, result = 0;
+ //wchar_t* testval = NULL;
+ void* key = NULL;
+ etch_string* newobj = NULL;
+ wchar_t* str0 = L"now ", *str1 = L"is ", *str2 = L"the ", *str3 = L"time";
+ wchar_t* strings[4] = { str0, str1, str2, str3 };
+ const size_t bytelen = (wcslen(str0) + 1) * sizeof(wchar_t);
+
+ for(; i < numitems; i++)
+ {
+ //testval = etch_malloc(bytelen, 0);
+ //memcpy(testval, strings[i], bytelen);
+ newobj = new_stringw(strings[i]);
+
+ key = etch_malloc(bytelen, 0);
+ memcpy(key, strings[i], bytelen);
+
+ result = ((struct i_hashtable*)((etch_object*)testhash)->vtab)->insert(testhash->realtable,
+ key, (int)bytelen, newobj, 0, NULL, NULL);
+ }
+
+ return numitems;
+}
+
+
+/*
+ * new_hashdata()
+ * create testhash hashtable and load it up with data objects
+ */
+static int new_hashdata(const int datatype)
+{
+ int count = 0;
+ testhash = new_hashtable(16);
+ count = load_hashdata_string();
+ return count;
+}
+
+
+/*
+ * destroy_hashdata()
+ * destroy testhash hashtable and content
+ */
+static void destroy_hashdata()
+{
+ destroy_hashtable(testhash, TRUE, TRUE);
+}
+
+
+/*
+ * test_iterator_over_arraylist
+ */
+static void test_iterator_over_arraylist(void)
+{
+ etch_iterator* iterator = NULL;
+ int testcount = 0, thiscount = 0;
+ testcount = new_listdata(0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(testlist);
+ CU_ASSERT_NOT_EQUAL(testlist->count, 0);
+
+ iterator = new_iterator(testlist, &testlist->iterable);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator);
+ CU_ASSERT_EQUAL_FATAL(iterator->ordinal,1);
+
+ while(iterator->has_next(iterator))
+ thiscount += (iterator->next(iterator) == 0);
+
+ CU_ASSERT_EQUAL(thiscount, testcount-1);
+
+ etch_object_destroy(iterator);
+ destroy_listdata();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_iterator_over_hashtable
+ */
+static void test_iterator_over_hashtable(void)
+{
+ etch_iterator* iterator = NULL;
+ int result = 0, thiscount = 0, testcount = 0;
+ testcount = new_hashdata(1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(testhash);
+ CU_ASSERT_NOT_EQUAL(((struct i_hashtable*)((etch_object*)testhash)->vtab)->count(testhash->realtable,0,0),0);
+#ifdef ETCH_DEBUGALLOC
+ startbytes = etch_showmem(0, FALSE); /* note testdata bytes */
+#endif
+
+ iterator = new_iterator(testhash, &testhash->iterable);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator->current_key);
+ CU_ASSERT_EQUAL_FATAL(iterator->ordinal,1);
+ thiscount = 1;
+
+ while(iterator->has_next(iterator))
+ {
+ if (0 != (result = iterator->next(iterator))) break;
+
+ thiscount++;
+ CU_ASSERT_PTR_NOT_NULL(iterator->current_key);
+ CU_ASSERT_PTR_NOT_NULL(iterator->current_value);
+
+ #if IS_DEBUG_CONSOLE
+ if (iterator->current_value)
+ wprintf(L"value is %s\n",(wchar_t*)iterator->current_value);
+ else wprintf(L"value is null\n");
+ #endif
+ }
+
+ CU_ASSERT_EQUAL(testcount, thiscount);
+
+ etch_object_destroy(iterator);
+ destroy_hashdata();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ g_bytes_allocated -= startbytes;
+ CU_ASSERT_TRUE(g_bytes_allocated <= 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_stack_allocated_iterator
+ * iterator as automatic variable
+ */
+static void test_stack_allocated_iterator(void)
+{
+ etch_iterator iterator;
+ struct i_iterable* vtab = NULL;
+ int testcount = 0, thiscount = 0;
+ testcount = new_listdata(0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(testlist);
+ CU_ASSERT_NOT_EQUAL(testlist->count, 0);
+
+
+ set_iterator(&iterator, testlist, &testlist->iterable);
+
+ CU_ASSERT_EQUAL_FATAL(iterator.ordinal,1);
+ vtab = (struct i_iterable*)iterator.object.vtab;
+ while(vtab->has_next(&iterator))
+ thiscount += (vtab->next(&iterator) == 0);
+
+ CU_ASSERT_EQUAL(thiscount, testcount-1);
+
+ destroy_listdata();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_reset_iterator
+ * test reinitalizing and reusing iterator
+ */
+static void test_reset_iterator(void)
+{
+ etch_iterator iterator;
+ struct i_iterable* vtab = NULL;
+ int testcount = 0, thiscount1 = 0, thiscount2 = 0;
+
+ testcount = new_listdata(0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(testlist);
+ CU_ASSERT_NOT_EQUAL(testlist->count, 0);
+
+ set_iterator(&iterator, testlist, &testlist->iterable);
+
+ CU_ASSERT_EQUAL(iterator.ordinal,1);
+ vtab = (struct i_iterable*)iterator.object.vtab;
+ while(vtab->has_next(&iterator))
+ thiscount1 += (vtab->next(&iterator) == 0);
+
+ CU_ASSERT_EQUAL(thiscount1, testcount-1);
+
+ set_iterator(&iterator, testlist, &testlist->iterable);
+ vtab = (struct i_iterable*)iterator.object.vtab;
+ CU_ASSERT_EQUAL(iterator.ordinal,1);
+
+ while(vtab->has_next(&iterator))
+ thiscount2 += (vtab->next(&iterator) == 0);
+
+ CU_ASSERT_EQUAL(thiscount2, thiscount1);
+
+ destroy_listdata();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_iterator_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("suite_iterator", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test forward iterator over arraylist", test_iterator_over_arraylist);
+ CU_add_test(pSuite, "test forward iterator over hashtable", test_iterator_over_hashtable);
+ CU_add_test(pSuite, "test stack allocated iterator", test_stack_allocated_iterator);
+ CU_add_test(pSuite, "test reset iterator", test_reset_iterator);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/etch-c-test.vcproj b/binding-c/runtime/c/src/test/etch-c-test.vcproj
new file mode 100644
index 0000000..274c9ad
--- /dev/null
+++ b/binding-c/runtime/c/src/test/etch-c-test.vcproj
@@ -0,0 +1,518 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="etch-c-test"
+ ProjectGUID="{64A36C3F-2BE4-4471-A291-C65BD8A9E6FE}"
+ RootNamespace="etchctest"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="target/win32/$(ConfigurationName)"
+ IntermediateDirectory="target/win32/$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets=".\etch-c-test.vsprops"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\include;..\extern\jenkhash;"$(APR)\include";"$(APR_ICONV)\include";"$(CUNIT)\CUnit\Headers""
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ ProgramDataBaseFileName="$(IntDir)\etch-c-test.pdb"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="libapr-1.lib libapriconv-1.lib libcunit.lib WS2_32.LIB Mswsock.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="$(APR)\Debug;$(APR_ICONV)\Debug;$(CUNIT)\VC8\Release - Static Lib\"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="target/win32/$(ConfigurationName)"
+ IntermediateDirectory="target/win32/$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets=".\etch-c-test.vsprops"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\include;..\extern\jenkhash;"$(APR)\include";"$(APR_ICONV)\include";"$(CUNIT)\CUnit\Headers""
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="libapr-1.lib libapriconv-1.lib libcunit.lib WS2_32.LIB Mswsock.lib"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="$(APR)\Release;$(APR_ICONV)\Release;$(CUNIT)\VC8\Release - Static Lib\"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Static|Win32"
+ OutputDirectory="target/win32/$(ConfigurationName)"
+ IntermediateDirectory="target/win32/$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets=".\etch-c-test.vsprops"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\include;..\extern\jenkhash;"$(APR)\include\win32_x86";"$(APR)\include";"$(APR_ICONV)\include";"$(CUNIT)\include""
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;APR_DECLARE_STATIC;API_DECLARE_STATIC"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ ProgramDataBaseFileName="$(IntDir)\etch-c-test.pdb"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/NODEFAULTLIB:LIBCMT"
+ AdditionalDependencies="apr-1.lib apriconv-1.lib cunit.lib WS2_32.LIB Mswsock.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories=""$(APR)\lib\win32_x86_Debug";"$(APR_ICONV)\lib\win32_x86_Debug";"$(CUNIT)\lib\win32_x86_Debug""
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release Static|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets=".\etch-c-test.vsprops"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Quelldateien"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\main.c"
+ >
+ </File>
+ <File
+ RelativePath=".\test_all.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_arraylist.c"
+ >
+ </File>
+ <File
+ RelativePath=".\message\test_arrayvalue.c"
+ >
+ </File>
+ <File
+ RelativePath=".\message\test_binarytditdo.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_cache.c"
+ >
+ </File>
+ <File
+ RelativePath=".\message\test_defvalufact.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_etch.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_etch_config.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_etch_encoding.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_etch_linked_list.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_etch_log.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_etch_mutex.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_etch_runtime.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_etch_thread.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_etch_wait.c"
+ >
+ </File>
+ <File
+ RelativePath=".\internal\test_etchinternal_single_linked_list.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_etchobject.c"
+ >
+ </File>
+ <File
+ RelativePath=".\message\test_field.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_flexbuf.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_hashing.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_hashtable.c"
+ >
+ </File>
+ <File
+ RelativePath=".\message\test_idname.c"
+ >
+ </File>
+ <File
+ RelativePath=".\common\test_iterator.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\test_mailboxmgr.c"
+ >
+ </File>
+ <File
+ RelativePath=".\message\test_message.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\test_messagizer.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\test_packetizer.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\test_plainmailbox.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\test_queue.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\test_remote.c"
+ >
+ </File>
+ <File
+ RelativePath=".\message\test_structvalue.c"
+ >
+ </File>
+ <File
+ RelativePath=".\support\test_stub.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\test_tcpconn.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\test_threadpool.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\test_transport.c"
+ >
+ </File>
+ <File
+ RelativePath=".\message\test_type.c"
+ >
+ </File>
+ <File
+ RelativePath=".\transport\test_url.c"
+ >
+ </File>
+ <File
+ RelativePath=".\message\test_validator.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Headerdateien"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\test_all.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Ressourcendateien"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/binding-c/runtime/c/src/test/etch-c-test.vsprops b/binding-c/runtime/c/src/test/etch-c-test.vsprops
new file mode 100644
index 0000000..9512adc
--- /dev/null
+++ b/binding-c/runtime/c/src/test/etch-c-test.vsprops
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="etch-c-test"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalLibraryDirectories=""
+ />
+ <UserMacro
+ Name="APR"
+ Value="$(SolutionDir)src\extern\apr\apr"
+ PerformEnvironmentSet="true"
+ />
+ <UserMacro
+ Name="APR_ICONV"
+ Value="$(SolutionDir)src\extern\apr\apr-iconv"
+ PerformEnvironmentSet="true"
+ />
+ <UserMacro
+ Name="CUNIT"
+ Value="$(SolutionDir)src\extern\cunit"
+ PerformEnvironmentSet="true"
+ />
+ <UserMacro
+ Name="VLD"
+ Value="$(SolutionDir)src\extern\vld"
+ PerformEnvironmentSet="true"
+ />
+</VisualStudioPropertySheet>
diff --git a/binding-c/runtime/c/src/test/internal/test_etchinternal_single_linked_list.c b/binding-c/runtime/c/src/test/internal/test_etchinternal_single_linked_list.c
new file mode 100644
index 0000000..7d4bce9
--- /dev/null
+++ b/binding-c/runtime/c/src/test/internal/test_etchinternal_single_linked_list.c
@@ -0,0 +1,51 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "CUnit.h"
+#include "etchinternal_single_linked_list.h"
+
+static int intFinder(void* data, void* toFind) {
+ int iToFind = (int)toFind;
+ int intData = *((int*)data);
+ return iToFind == intData;
+}
+
+static void testFind() {
+ etchinternal_single_linked_list* l = etchinternal_single_linked_list_create();
+
+ int v = 1;
+ etchinternal_single_linked_list_add(l, &v, sizeof(v));
+ v = 2;
+ etchinternal_single_linked_list_add(l, &v, sizeof(v));
+
+ CU_ASSERT_EQUAL(1, etchinternal_single_linked_list_find(l, intFinder, (void*)1) != 0);
+ CU_ASSERT_EQUAL(1, etchinternal_single_linked_list_find(l, intFinder, (void*)2) != 0);
+ CU_ASSERT_EQUAL(0, etchinternal_single_linked_list_find(l, intFinder, (void*)3) != 0);
+
+ etchinternal_single_linked_list_destroy(l);
+}
+
+CU_pSuite test_etchinternal_single_linked_list()
+{
+ CU_pSuite pSuite = CU_add_suite("etchinternal_single_linked_list", 0, 0);
+ CU_add_test(pSuite, "testFind()", testFind);
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/jenktest.txt b/binding-c/runtime/c/src/test/jenktest.txt
new file mode 100644
index 0000000..1183597
--- /dev/null
+++ b/binding-c/runtime/c/src/test/jenktest.txt
@@ -0,0 +1,58 @@
+apple
+banana
+banter
+baseball
+blueberry
+brown
+browser
+camera
+can
+cantaloupe
+cherry
+cinnamon
+clear
+dash
+date
+distance
+ever
+ewe
+eye
+fig
+grape
+her
+lake
+leaf
+leave
+lemon
+lever
+lime
+mellon
+mango
+math
+music
+netscape
+never
+orange
+pear
+pepper
+politics
+psychedelic
+psychology
+salt
+science
+sift
+signals
+softball
+spice
+stupid
+swear
+syntax
+strawberry
+tangerine
+towel
+turn
+washcloth
+watermelon
+will
+words
+yellow
diff --git a/binding-c/runtime/c/src/test/main.c b/binding-c/runtime/c/src/test/main.c
new file mode 100644
index 0000000..2df5364
--- /dev/null
+++ b/binding-c/runtime/c/src/test/main.c
@@ -0,0 +1,109 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifdef _VLD_H_
+#include "vld.h"
+#endif
+
+#include "test_all.h"
+#include <stdlib.h>
+#include <string.h>
+#include "CUnit.h"
+#include "Basic.h"
+#include "Automated.h"
+#include "Console.h"
+#include <stdio.h>
+#include <apr.h>
+#include "apr_time.h"
+
+void run_console(int argc, char** args) {
+ CU_console_run_tests();
+}
+
+void run_automatic(int argc, char** args) {
+ CU_set_output_filename("alltests");
+ CU_automated_run_tests();
+}
+
+void run_selected(int argc, char** args) {
+ int i;
+ CU_pTestRegistry reg;
+ CU_pSuite suiteToRun;
+ reg = CU_get_registry();
+ for (i=1; i<argc; i++) {
+ if (args[i][0] != '-') {
+ suiteToRun = CU_get_suite_by_name(args[i], reg);
+ if (suiteToRun) {
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_suite(suiteToRun);
+ } else {
+ printf("suite '%s' not found\n", args[i]);
+ }
+ }
+ }
+}
+
+void run_default(int arg, char** args) {
+ char c;
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+
+ printf("press <enter> to exit.");
+ scanf("%c", &c);
+}
+
+void (*runner)(int argc, char** args);
+
+void parseOptions(int argc, char** args) {
+ runner = run_default;
+ if (argc >= 2) {
+ char* parameter = args[1];
+ if (strcmp(parameter, "-c") == 0) {
+ runner = run_console;
+ } else if (strcmp(parameter, "-a") == 0) {
+ runner = run_automatic;
+ } else if (strcmp(parameter, "-s") == 0) {
+ runner = run_selected;
+ }
+ }
+}
+
+int main(int argc, char** argv) {
+ int i=0;
+ CU_ErrorCode rv = 0;
+
+ parseOptions(argc, argv);
+
+ if (CUE_SUCCESS != CU_initialize_registry()) {
+ return 1;
+ }
+
+ while (etch_testlist[i] != 0) {
+ (void)etch_testlist[i++]();
+ }
+
+ runner(argc, argv);
+
+ CU_cleanup_registry();
+ rv = CU_get_error();
+
+ return rv;
+}
diff --git a/binding-c/runtime/c/src/test/message/test_arrayvalue.c b/binding-c/runtime/c/src/test/message/test_arrayvalue.c
new file mode 100644
index 0000000..bea11bf
--- /dev/null
+++ b/binding-c/runtime/c/src/test/message/test_arrayvalue.c
@@ -0,0 +1,1746 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_arrayvalue.c -- test etch_arrayvalue object
+ */
+#include "etch_runtime.h"
+#include "etch_tagdata_inp.h"
+#include "etch_arrayval.h"
+#include "etch_exception.h"
+#include "etch_nativearray.h"
+#include "etch_objecttypes.h"
+#include "etch_mem.h"
+#include <wchar.h>
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+#define OBJTYPE_FAKETDI_IMPL ETCHTYPEB_INSTANCEDATA
+#define CLASSID_FAKETDI_IMPL 0xf0
+#define OBJTYPE_FAKETDO_IMPL ETCHTYPEB_INSTANCEDATA
+#define CLASSID_FAKETDO_IMPL 0xf1
+#define OBJTYPE_FAKETDI_VTABLE 0x88
+#define OBJTYPE_FAKETDO_VTABLE 0x89
+
+#define EXCPTEST_UNCHECKED_STATICTEXT 1
+#define EXCPTEST_CHECKED_COPYTEXT 2
+#define EXCPTEST_CHECKED_STATICTEXT 3
+
+#define IS_DEBUG_CONSOLE FALSE /* TRUE to display test diagnostics */
+
+static etch_arraylist* testdata;
+static int g_which_exception_test;
+
+
+#if(0)
+
+/**
+ * fake_tdi_impl
+ * instance data for a TDI implementation
+ */
+typedef struct fake_tdi_impl
+{
+ etch_object object;
+
+ int index;
+ byte started, done, ended;
+ etch_arrayvalue* arrray;
+
+} fake_tdi_impl;
+
+
+/**
+ * fake_tdo_impl
+ * instance data for a TDO implementation
+ */
+typedef struct fake_tdo_impl
+{
+ etch_object object;
+
+ byte started, ended;
+ etch_arraylist* list;
+ etch_arrayvalue* arrray;
+
+} fake_tdo_impl;
+
+
+/**
+ * destroy_fake_tdi_impl
+ * memory cleanup handler for fake_tdi_impl
+ */
+static int destroy_fake_tdi_impl(fake_tdi_impl* impl)
+{
+ int result = verify_object((etch_object*)impl, OBJTYPE_FAKETDI_IMPL, CLASSID_FAKETDI_IMPL, NULL);
+ if (result == -1) return -1; /* object passed was not expected object */
+
+ etch_object_destroy(impl->arrray);
+
+ etch_free(impl);
+ return 0;
+}
+
+
+/**
+ * destroy_fake_tdo_impl
+ * memory cleanup handler for fake_tdo_impl
+ */
+static int destroy_fake_tdo_impl(fake_tdo_impl* impl)
+{
+ etch_destructor destroy = NULL;
+ int result = verify_object((etch_object*)impl, OBJTYPE_FAKETDO_IMPL, CLASSID_FAKETDO_IMPL, NULL);
+ if (result == -1) return -1; /* object passed was not expected object */
+
+ impl->list->is_readonly = TRUE;
+ etch_object_destroy(impl->list); /* destroy list, but not content */
+
+ impl->arrray->list->is_readonly = FALSE;
+ etch_object_destroy(impl->arrray); /* destroy arravalue and content */
+
+ etch_free(impl);
+ return 0;
+}
+
+
+/**
+ * new_fake_tdi_impl()
+ * constructor for TDI implementation instance data
+ */
+static fake_tdi_impl* new_fake_tdi_impl()
+{
+ fake_tdi_impl* data = (fake_tdi_impl*) new_object
+ (sizeof(fake_tdi_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDI_IMPL);
+
+ data->destroy = destroy_fake_tdi_impl;
+
+ return data;
+}
+
+
+/**
+ * new_fake_tdo_impl()
+ * constructor for TDO implementation instance data
+ */
+static fake_tdo_impl* new_fake_tdo_impl()
+{
+ fake_tdo_impl* data = (fake_tdo_impl*) new_object
+ (sizeof(fake_tdo_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDO_IMPL);
+
+ data->destroy = destroy_fake_tdo_impl;
+
+ return data;
+}
+
+
+static enum etch_classid CLASSID_VTABLE_FAKETDI = CLASSID_DYNAMIC_START + 0x0;
+static enum etch_classid CLASSID_VTABLE_FAKETDO = CLASSID_DYNAMIC_START + 0x1;
+
+
+/**
+ * faketdi_start_array() overrides tdi_start_array()
+ * Starts reading an array from the stream.
+ * @return the array that we are reading.
+ * @throws IOException if there is a problem with the stream.
+ */
+static etch_arrayvalue* faketdi_start_array(tagged_data_input* tdi)
+{
+ int result = 0;
+ fake_tdi_impl* data = NULL;
+ etch_arrayvalue* newarrayval = NULL;
+ const int DIM_ZERO = 0, READONLY = TRUE, DEFSIZE = 0, DEFDELTA = 0;
+ byte fake_typecode = 0;
+ etch_type* fake_structtype = NULL;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+
+ data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = data->destroy(NULL); /* ensure we can call instance data destructor */
+ CU_ASSERT_EQUAL(result,-1);
+
+ CU_ASSERT_EQUAL(data->started,FALSE);
+ CU_ASSERT_EQUAL(data->done,FALSE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+
+ data->started = TRUE;
+ data->index = 0;
+
+ /* create instance array */
+ newarrayval = new_arrayvalue
+ (fake_typecode, fake_structtype, DIM_ZERO, DEFSIZE, DEFDELTA, READONLY);
+
+ data->arrray = newarrayval;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->arrray);
+
+ return data->arrray;
+}
+
+
+/**
+ * faketdo_start_array() overrides tdo_start_array()
+ */
+static etch_arrayvalue* faketdo_start_array(tagged_data_output* tdo, etch_arrayvalue* thisp)
+{
+ int result = 0;
+ fake_tdo_impl* data = NULL;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+
+ data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = data->destroy(NULL); /* ensure we can call into instance data destructor */
+ CU_ASSERT_EQUAL(result,-1);
+
+ CU_ASSERT_EQUAL(data->started,FALSE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+
+ data->arrray = thisp;
+ data->list = new_arrayvalue_arraylist();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->arrray);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->list);
+
+ data->started = TRUE;
+ return data->arrray;
+}
+
+
+/**
+ * faketdi_read_array_element() overrides tdi_read_array_element()
+ */
+static int faketdi_read_array_element(tagged_data_input* tdi, ETCH_ARRAY_ELEMENT** out_ae)
+{
+ int result = 0;
+ fake_tdi_impl* data = NULL;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+
+ data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->done,FALSE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+
+ if (data->index < (int)testdata->count)
+ {
+ *out_ae = arraylist_get(testdata, data->index++);
+ return TRUE;
+ }
+
+ data->done = TRUE;
+ return FALSE;
+}
+
+
+/**
+ * faketdo_write_array_element() overrides tdi_read_array_element()
+ */
+static int faketdo_write_array_element(tagged_data_output* tdo, etch_object* newitem)
+{
+ int result = 0;
+ fake_tdo_impl* data = NULL;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+
+ data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->list);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+
+ result = arraylist_add(data->list, newitem);
+ return 0;
+}
+
+
+/**
+ * faketdi_end_array() overrides tdi_end_array()
+ * Ends an array that we are reading.
+ * @param array the array that we read.
+ * @throws IOException if there is a problem with the stream.
+ */
+static int faketdi_end_array(tagged_data_input* tdi, etch_arrayvalue* av)
+{
+ int result = 0;
+ fake_tdi_impl* data = NULL;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+
+ data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->arrray);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->done,TRUE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+ CU_ASSERT_EQUAL(data->arrray,av);
+
+ data->ended = TRUE;
+ return 0;
+}
+
+
+/**
+ * faketdo_end_array() overrides tdo_end_array()
+ */
+static int faketdo_end_array(tagged_data_output* tdo, etch_arrayvalue* av)
+{
+ int result = 0;
+ fake_tdo_impl* data = NULL;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+
+ data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->list);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->arrray);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+ CU_ASSERT_EQUAL(data->arrray,av);
+
+ data->ended = TRUE;
+ return 0;
+}
+
+
+/**
+ * faketdi_close() port from java test
+ * also tested here is that we can call base methods on a derived object. we invoke
+ * the TDI destructor here, which is an overide of the etchobject destructor.
+ * the TDI destructor walks the vtable chain to its parent, and invokes the parent
+ * destructor to destroy etchobject content such as any exception, and finally
+ * the object itself.
+ */
+static void faketdi_close(tagged_data_input* tdi)
+{
+ etch_object_destroy(tdi); /* destroy object */
+}
+
+
+/**
+ * faketdo_close() port from java test
+ */
+static void faketdo_close(tagged_data_output* tdo)
+{
+ etch_object_destroy(tdo); /* destroy object */
+}
+
+
+/**
+ * new_fake_tdi()
+ * constructor for TDI implementation
+ */
+static tagged_data_input* new_fake_tdi()
+{
+ tagged_data_input* faketdi = NULL;
+ i_tagged_data_input* vtab = NULL;
+ const unsigned short CLASS_ID = CLASSID_VTABLE_FAKETDI;
+
+ faketdi = new_tagged_data_input();
+
+ vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+ if(!vtab)
+ {
+ vtab = new_vtable(NULL, sizeof(i_tagged_data_input), CLASS_ID);
+
+ /* override three i_tagged_data_input methods */
+ vtab->start_array = faketdi_start_array;
+ vtab->end_array = faketdi_end_array;
+ vtab->read_array_element = faketdi_read_array_element;
+
+ ((etch_object*)vtab)->vtab = faketdi->vtab; /* chain parent vtable to override vtab */
+
+ cache_insert(vtab->hashkey, vtab, FALSE);
+ }
+
+ CU_ASSERT_EQUAL_FATAL(((etch_object*)vtab)->class_id, CLASSID_VTABLE_FAKETDI);
+
+ ((etch_object*)faketdi)->vtab = vtab; /* set override vtab */
+
+ faketdi->impl = (etch_object*) new_fake_tdi_impl(); /* instantiate tdi instance data */
+
+ switch(g_which_exception_test)
+ { case EXCPTEST_UNCHECKED_STATICTEXT:
+ etch_throw((etch_object*)faketdi, EXCPTYPE_NULLPTR, NULL, 0);
+ break;
+ case EXCPTEST_CHECKED_COPYTEXT:
+ etch_throw((etch_object*)faketdi, EXCPTYPE_CHECKED_BOGUS, L"copied text", ETCHEXCP_COPYTEXT | ETCHEXCP_FREETEXT);
+ break;
+ case EXCPTEST_CHECKED_STATICTEXT:
+ etch_throw((etch_object*)faketdi, EXCPTYPE_CHECKED_BOGUS, local_excp_text, ETCHEXCP_STATICTEXT);
+ break;
+ }
+
+ return faketdi;
+}
+
+
+/**
+ * new_fake_tdo()
+ * constructor for TDO implementation
+ */
+static tagged_data_output* new_fake_tdo()
+{
+ tagged_data_output* faketdo = NULL;
+ i_tagged_data_output* vtab = NULL;
+ const unsigned short CLASS_ID = CLASSID_VTABLE_FAKETDO;
+
+ faketdo = new_tagged_data_output();
+
+ vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+ if(!vtab)
+ { vtab = new_vtable(NULL, sizeof(i_tagged_data_input), CLASS_ID);
+
+ /* override three i_tagged_data_output methods */
+ vtab->start_array = faketdo_start_array;
+ vtab->end_array = faketdo_end_array;
+ vtab->write_array_element = faketdo_write_array_element;
+
+ ((etch_object*)vtab)->vtab = faketdo->vtab; /* chain parent vtab to override vtab */
+
+ cache_insert(vtab->hashkey, vtab, FALSE);
+ }
+
+ CU_ASSERT_EQUAL_FATAL(((etch_object*)vtab)->class_id, CLASSID_VTABLE_FAKETDO);
+
+ ((etch_object*)faketdo)->vtab = vtab; /* set override vtab */
+
+ faketdo->impl = (etch_object*) new_fake_tdo_impl(); /* instantiate tdo instance data */
+
+ return faketdo;
+}
+
+#endif /* if(0) */
+
+
+/*
+ * load_testdata_string()
+ * load testdata array with some ETCH_STRING objects
+ */
+static int load_testdata_string()
+{
+ int i = 0, numitems = 4;
+ etch_string* newobj = NULL;
+ wchar_t* str0 = L"now ", *str1 = L"is ", *str2 = L"the ", *str3 = L"time";
+ wchar_t* strings[4] = { str0, str1, str2, str3 };
+
+ for(; i < numitems; i++)
+ {
+ /* new_etch_string copies parameter string */
+ newobj = new_stringw(strings[i]);
+ etch_arraylist_add(testdata, newobj);
+ }
+
+ return numitems;
+}
+
+
+/*
+ * load_testdata_int()
+ * load testdata array with some ETCH_INT objects
+ */
+static int load_testdata_int()
+{
+ int i = 0, numitems = 4;
+ etch_int32* newobj = NULL;
+ int ints[4] = { 1, 2, 3, 4 };
+
+ for(; i < numitems; i++)
+ {
+ newobj = new_int32(ints[i]);
+ etch_arraylist_add(testdata, newobj);
+ }
+
+ return numitems;
+}
+
+
+/*
+ * new_testdata()
+ * create testdata array and load it up with data objects
+ */
+static int new_testdata(const int datatype)
+{
+ int count = 0;
+ g_which_exception_test = 0;
+ testdata = new_etch_arraylist(0,0);
+ testdata->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
+
+ switch(datatype)
+ { case 1: count = load_testdata_int(); break;
+ case 2: count = load_testdata_string(); break;
+ default: return -1;
+ }
+
+ return count;
+}
+
+
+/*
+ * destroy_testdata()
+ * destroy testdata array and content
+ */
+static void destroy_testdata()
+{
+ etch_arraylist_destroy(testdata, TRUE);
+}
+
+
+#if 0
+/*
+ * compare_lists()
+ * compares testdata list with the arrayvalue's list
+ * returns boolean indicating equal or not
+ */
+static int compare_lists(etch_arrayvalue* av)
+{
+ int testcount = 0, i = 0, eqcount = 0;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av->list);
+
+ testcount = av->list->count;
+ CU_ASSERT_EQUAL(testcount, testdata->count);
+ if (testcount != testdata->count) return FALSE;
+
+ for(; i < (const int) testcount; i++)
+ if (etch_arraylist_get(testdata, i) == etch_arraylist_get(av->list, i))
+ eqcount++;
+
+ CU_ASSERT_EQUAL(testcount, eqcount);
+ return testcount == eqcount;
+}
+#endif
+
+#if(0)
+
+/*
+ * run_read_test
+ * create an array value to read current test data
+ * and verify that content matches test data
+ */
+static void run_read_test()
+{
+ int result = 0;
+ tagged_data_input* tdi = NULL;
+ etch_arrayvalue* av = NULL;
+
+ tdi = new_fake_tdi();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+ CU_ASSERT_FALSE_FATAL(is_exception(tdi));
+ CU_ASSERT_EQUAL_FATAL(get_result(tdi),0);
+
+ /* create array value, reading from test values */
+ av = arrayvalue_read(tdi);
+
+ result = compare_lists(av);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ /* clean up TDI */
+ faketdi_close(tdi);
+
+ /* destroy testdata list and content */
+ testdata->destroy(testdata);
+
+ g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ memtable_clear(); /* start fresh for next test */
+}
+
+
+/*
+ * run_write_test
+ * create an array value to write current test data
+ * and verify that content matches test data
+ */
+static void run_write_test()
+{
+ int result = 0, i = 0;
+ etch_arrayvalue* arrayval = NULL;
+ tagged_data_output* tdo = NULL;
+ const int DIM_ZERO = 0, NOT_READONLY = FALSE, DEFSIZE = 0, DEFDELTA = 0;
+ etch_type* fake_structtype = NULL;
+ byte fake_typecode = 0;
+ void* thisitem = NULL;
+
+ arrayval = new_arrayvalue /* create array value instance */
+ (fake_typecode, fake_structtype, DIM_ZERO, DEFSIZE, DEFDELTA, NOT_READONLY);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(arrayval);
+ CU_ASSERT_FALSE_FATAL(is_exception(arrayval));
+ CU_ASSERT_EQUAL_FATAL(get_result(arrayval),0);
+
+ for(; i < (const int) testdata->count; i++)
+ {
+ thisitem = arraylist_get(testdata, i);
+ result = arrayvalue_add(arrayval, (ETCH_ARRAY_ELEMENT*) thisitem);
+ CU_ASSERT_EQUAL(result,0);
+ }
+
+ tdo = new_fake_tdo();
+
+ result = arrayvalue_write(arrayval, tdo);
+ CU_ASSERT_EQUAL(result,0);
+
+ faketdo_close(tdo);
+
+ /* destroy testdata list but not content */
+ arraylist_destroy(testdata, FALSE);
+
+ g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ memtable_clear(); /* start fresh for next test */
+}
+
+#endif /* #if(0) */
+
+
+/*
+ * run_exception_test
+ *
+ */
+static void run_exception_test(const int whichtest)
+{
+ /* global marker asks components to throw exceptions */
+ g_which_exception_test = whichtest;
+
+ #if(0)
+ /* create a bogus TDI inheriting from tagged data input */
+ tdi = new_fake_tdi();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+
+ do
+ { switch(whichtest)
+ { case EXCPTEST_UNCHECKED_STATICTEXT:
+ case EXCPTEST_CHECKED_COPYTEXT:
+ case EXCPTEST_CHECKED_STATICTEXT:
+ { CU_ASSERT_TRUE_FATAL(is_exception(tdi));
+ #if IS_DEBUG_CONSOLE
+ wprintf(L"\ncaught %s exception\n", tdi->result->exception->excptext);
+ #endif
+ break;
+ }
+ }
+ if (is_exception(tdi)) break;
+
+ /* create array value, reading from test values */
+ av = arrayvalue_read(tdi);
+ /* if av is NULL we would throw an exception here */
+
+ result = compare_lists(av);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ } while(0);
+
+ /* clean up TDI */
+ faketdi_close(tdi);
+
+ #endif
+
+ /* destroy testdata list and content */
+ etch_object_destroy(testdata);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+#if(0)
+
+/*
+ * read_test_integer
+ */
+static void read_test_integer(void)
+{
+ int itemcount = 0;
+ if ((itemcount = new_testdata(1)) > 0)
+ run_read_test();
+}
+
+
+/*
+ * write_test_integer
+ */
+static void write_test_integer(void)
+{
+ int itemcount = 0;
+ if ((itemcount = new_testdata(1)) > 0)
+ run_write_test();
+}
+
+
+/*
+ * read_test_string
+ */
+static void read_test_string(void)
+{
+ int itemcount = 0;
+ if ((itemcount = new_testdata(2)) > 0)
+ run_read_test();
+}
+
+
+/*
+ * write_test_string
+ */
+static void write_test_string(void)
+{
+ int itemcount = 0;
+ if ((itemcount = new_testdata(2)) > 0)
+ run_write_test();
+}
+
+#endif /* #if(0) */
+
+
+/*
+ * exception_test_1
+ */
+static void exception_test_1(void)
+{
+ int itemcount = 0;
+ if ((itemcount = new_testdata(1)) > 0)
+ run_exception_test(EXCPTEST_UNCHECKED_STATICTEXT);
+}
+
+
+/*
+ * exception_test_2
+ */
+static void exception_test_2(void)
+{
+ int itemcount = 0;
+ if ((itemcount = new_testdata(1)) > 0)
+ run_exception_test(EXCPTEST_CHECKED_COPYTEXT);
+}
+
+
+/*
+ * exception_test_3
+ */
+static void exception_test_3(void)
+{
+ int itemcount = 0;
+ if ((itemcount = new_testdata(1)) > 0)
+ run_exception_test(EXCPTEST_CHECKED_STATICTEXT);
+}
+
+
+/*
+ * test_iterator_over_arraylist
+ */
+static void test_iterator_over_arraylist(void)
+{
+ etch_iterator* iterator = NULL;
+ int testcount = 0;
+ struct i_iterable* vtab = NULL;
+
+ new_testdata(1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(testdata);
+ CU_ASSERT_NOT_EQUAL(testdata->count, 0);
+
+ iterator = new_iterator(testdata, &testdata->iterable);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator);
+ CU_ASSERT_EQUAL_FATAL(iterator->ordinal,1);
+ testcount = 1;
+
+ vtab = (struct i_iterable*)((etch_object*)iterator)->vtab;
+
+ while(vtab->has_next(iterator))
+ testcount += vtab->next(iterator) == 0;
+
+ CU_ASSERT_EQUAL(testcount, testdata->count);
+
+ destroy_iterator(iterator);
+ destroy_testdata();
+}
+
+
+/*
+ * test_create_from_1
+ * create arrayvalue from single-dimensioned byte nativearray and verify
+ */
+static void test_create_from_1(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_byte* thisitem = NULL;
+ int i = 0, result = 0;
+ byte fake_typecode = 0xff;
+ char x[4] = {'a','b','c','d'};
+ const int numdimensions = 1, itemcount = 4;
+
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, itemcount, 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_BYTE; /* array content is type byte */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+
+ for(i = 0; i < itemcount; i++) /* populate native array from test data */
+ CU_ASSERT_EQUAL(0, result = a->put1(a, &x[i], i));
+
+ /* populate arrayvalue from native array */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);
+
+ /* assert that arrayvalue content matches test data */
+ for(i=0; i < itemcount; i++)
+ {
+ thisitem = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_PRIMITIVE_BYTE);
+ CU_ASSERT_EQUAL(thisitem->value, x[i]);
+ }
+
+ etch_object_destroy(av); /* destroy array value and content */
+}
+
+
+/*
+ * test_create_from_2
+ * create arrayvalue from single-dimensioned int nativearray and verify
+ */
+static void test_create_from_2(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_int32* thisitem = NULL;
+ int i = 0, result = 0;
+ byte fake_typecode = 0xff;
+ int n[4] = {10,11,12,13};
+ const int numdimensions = 1, itemcount = 4;
+
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_INT32, sizeof(int), numdimensions, itemcount, 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_INT32; /* array content is type int */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+
+ for(i = 0; i < itemcount; i++) /* populate native array from test data */
+ CU_ASSERT_EQUAL(0, result = a->put1(a, &n[i], i));
+
+ /* populate arrayvalue from native array */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);
+
+ /* assert that arrayvalue content matches test data */
+ for(i=0; i < itemcount; i++)
+ {
+ thisitem = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_PRIMITIVE_INT32);
+ CU_ASSERT_EQUAL(thisitem->value, n[i]);
+ }
+
+ etch_object_destroy(av); /* destroy array value and content */
+}
+
+
+/*
+ * test_create_from_3
+ * create arrayvalue from single-dimensioned long long nativearray and verify
+ */
+static void test_create_from_3(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_int64* thisitem = NULL;
+ int i = 0, result = 0;
+ byte fake_typecode = 0xff;
+ int64 n[5] = {-2, -1, 0 , 1, 2};
+ const int numdimensions = 1, itemcount = 5;
+
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_INT64, sizeof(int64), numdimensions, itemcount, 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_INT64; /* array content is type int64 */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+
+ for(i = 0; i < itemcount; i++) /* populate native array from test data */
+ CU_ASSERT_EQUAL(0, result = a->put1(a, &n[i], i));
+
+ /* populate arrayvalue from native array */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);
+
+ /* assert that arrayvalue content matches test data */
+ for(i=0; i < itemcount; i++)
+ {
+ thisitem = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_PRIMITIVE_INT64);
+ CU_ASSERT_EQUAL(thisitem->value, n[i]);
+ }
+
+ etch_object_destroy(av); /* destroy array value and content */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_create_from_4
+ * create arrayvalue from single-dimensioned double nativearray and verify
+ */
+static void test_create_from_4(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_double* thisitem = NULL;
+ int i = 0, result = 0;
+ byte fake_typecode = 0xff;
+ double f[3] = {-1000.0, 3.14149, 65536.0};
+ const int numdimensions = 1, itemcount = 3;
+
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_DOUBLE, sizeof(int64), numdimensions, itemcount, 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_IEEE64; /* array content is type double */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+
+ for(i = 0; i < itemcount; i++) /* populate native array from test data */
+ CU_ASSERT_EQUAL(0, result = a->put1(a, &f[i], i));
+
+ /* populate arrayvalue from native array */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);
+
+ /* assert that arrayvalue content matches test data */
+ for(i=0; i < itemcount; i++)
+ {
+ thisitem = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_PRIMITIVE_DOUBLE);
+ CU_ASSERT_DOUBLE_EQUAL(thisitem->value, f[i], 0.01);
+ }
+
+ etch_object_destroy(av); /* destroy array value and content */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_create_from_5
+ * create arrayvalue from single-dimensioned string nativearray and verify
+ */
+static void test_create_from_5(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_string* thisitem = NULL;
+ int i = 0, result = 0;
+ byte fake_typecode = 0xff;
+ wchar_t* c[3] = {L"hey", L"it", L"works!"};
+ //char* c[3] = {"hey", "it", "works!"};
+ const int numdimensions = 1, itemcount = 3;
+
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_STRING, sizeof(void*), numdimensions, itemcount, 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_STRING; /* array content is type string */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+ a->is_content_owned = FALSE;
+
+ for(i = 0; i < itemcount; i++) /* populate native array from test data */
+ CU_ASSERT_EQUAL(0, result = a->put1(a, &c[i], i));
+
+ /* populate arrayvalue from native array */
+ printf("tot1\n");
+ av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
+ printf("tot2\n");
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);
+
+ /* assert that arrayvalue content matches test data */
+ for(i=0; i < itemcount; i++)
+ {
+ thisitem = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_STRING);
+ CU_ASSERT_NSTRING_EQUAL(thisitem->v.value, c[i], wcslen(c[i]));
+ //CU_ASSERT_NSTRING_EQUAL(thisitem->v.value, c[i], strlen(c[i]));
+ }
+
+ etch_object_destroy(av); /* destroy array value and content */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_create_from_10
+ * create arrayvalue from two-dimensioned byte nativearray and verify
+ */
+static void test_create_from_10(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_arrayvalue* subav = NULL;
+ etch_byte* thisitem = NULL;
+ int i = 0, j = 0, result = 0;
+ byte fake_typecode = 0xff;
+ char x[2][4] = { {'a','b','c','d',}, {'e', 'f', 'g', 'h',}, };
+ const int numdimensions = 2, dim0count = 4, dim1count = 2;
+
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, dim0count, dim1count, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_BYTE; /* array content is type byte */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+
+ for(i = 0; i < dim1count; i++)
+ for(j = 0; j < dim0count; j++) /* populate native array from test data */
+ CU_ASSERT_EQUAL(0, result = a->put2(a, &x[i][j], i, j));
+
+ /* populate arrayvalue from native array. we tell the arrayvalue to take
+ * ownership of the native array. result is a 2-dimensional arrayvalue,
+ * which is represented as an arrayvalue of arrayvalues
+ */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, 32, 0, FALSE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(av->dim, numdimensions);
+ CU_ASSERT_EQUAL(arrayvalue_count(av), dim1count);
+
+ /* assert that arrayvalue content matches test data */
+ for(i = 0; i < dim1count; i++)
+ {
+ subav = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(subav);
+ CU_ASSERT_EQUAL(((etch_object*)subav)->obj_type, ETCHTYPEB_ARRAYVAL);
+ CU_ASSERT_EQUAL(arrayvalue_count(subav), dim0count);
+
+ for(j = 0; j < dim0count; j++)
+ {
+ byte expected_value = x[i][j];
+ thisitem = arrayvalue_get(subav, j);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_PRIMITIVE_BYTE);
+ CU_ASSERT_EQUAL(thisitem->value, expected_value);
+ }
+ }
+
+ /* destroy array value and content. memory includes the sub-arrayvalues,
+ * the native array from above, and the sub-native arrays */
+ etch_object_destroy(av);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_create_from_11
+ * create arrayvalue from two-dimensioned int16 nativearray and verify
+ */
+static void test_create_from_11(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_arrayvalue* subav = NULL;
+ etch_int16* thisitem = NULL;
+ int i = 0, j = 0, result = 0;
+ byte fake_typecode = 0xff;
+ short n[2][4] = { { 100,101,102,103, }, { 0xeffc,0xeffd,0xeffe,0xefff }, };
+ const int numdimensions = 2, dim0count = 4, dim1count = 2;
+
+ etch_nativearray* a = new_etch_nativearray
+ (CLASSID_ARRAY_INT16, sizeof(short), numdimensions, dim0count, dim1count, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_INT16; /* array content is type short int */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+
+ for(i = 0; i < dim1count; i++)
+ for(j = 0; j < dim0count; j++) /* populate native array from test data */
+ CU_ASSERT_EQUAL(0, result = a->put2(a, &n[i][j], i, j));
+
+ /* populate arrayvalue from native array. we tell the arrayvalue to take
+ * ownership of the native array. result is a 2-dimensional arrayvalue,
+ * which is represented as an arrayvalue of arrayvalues
+ */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, 32, 0, FALSE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(av->dim, numdimensions);
+ CU_ASSERT_EQUAL(arrayvalue_count(av), dim1count);
+
+ /* assert that arrayvalue content matches test data */
+ for(i = 0; i < dim1count; i++)
+ {
+ subav = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(subav);
+ CU_ASSERT_EQUAL(((etch_object*)subav)->obj_type, ETCHTYPEB_ARRAYVAL);
+ CU_ASSERT_EQUAL(arrayvalue_count(subav), dim0count);
+
+ for(j = 0; j < dim0count; j++)
+ {
+ short expected_value = n[i][j];
+ thisitem = arrayvalue_get(subav, j);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_PRIMITIVE_INT16);
+ CU_ASSERT_EQUAL(thisitem->value, expected_value);
+ }
+ }
+
+ /* destroy array value and content. memory includes the sub-arrayvalues,
+ * the native array from above, and the sub-native arrays */
+ etch_object_destroy(av);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_create_from_12
+ * create arrayvalue from two-dimensioned string nativearray and verify
+ */
+static void test_create_from_12(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_arrayvalue* subav = NULL;
+ etch_string* thisitem = NULL;
+ const int DEFSIZE=0, DEFDELTA=0;
+ int i = 0, j = 0, result = 0;
+ byte fake_typecode = 0xff;
+
+ wchar_t* x[6][4]
+ = { { L"I think that I", L"shall never see,", L"A poem as lovely", L"as a tree.", },
+ { L"She knows not what", L"the curse may be,", L"And so she weaveth", L"steadily,", },
+ { L"And little other", L"care hath she,", L"The Lady of Shalott", L".", },
+ { L"And therefore I have sailed", L"the seas, and come", L"To the holy city", L"of Byzantium.", },
+ { L"Once out of nature", L"I shall never take", L"My bodily form", L" from any natural thing,", },
+ { L"But such a form", L"as Grecian goldsmiths make", L"Of hammered gold ", L"and gold enameling", },
+ };
+
+ const int numdimensions = 2, dim0count = 4, dim1count = 6;
+
+ #if(0) /* create empty native array and populate it item by item */
+
+ etch_nativearray* a = new_etch_nativearray (CLASSID_ARRAY_STRING,
+ sizeof(void*), numdimensions, dim0count, dim1count, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_STRING; /* array content is type string */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+
+ for(i = 0; i < dim1count; i++)
+ for(j = 0; j < dim0count; j++) /* populate native array from test data */
+ CU_ASSERT_EQUAL(0, result = a->put2(a, &x[i][j], i, j));
+
+ #else /* create native array from static array */
+
+ etch_nativearray* a = new_etch_nativearray_from(&x, CLASSID_ARRAY_STRING,
+ sizeof(void*), numdimensions, dim0count, dim1count, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_STRING; /* array content is type string */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+
+ #endif
+
+ for(j = 0; j < dim0count; j++)
+ { wchar_t* thisx = 0, *thatx = x[i][j];
+ result = a->get2(a, &thisx, i, j);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisx);
+ result = wcscmp(thisx, thatx);
+ CU_ASSERT_EQUAL(result,0);
+ }
+
+ /* populate arrayvalue from native array. we tell the arrayvalue to take
+ * ownership of the native array. result is a 2-dimensional arrayvalue,
+ * which is represented as an arrayvalue of arrayvalues
+ */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, DEFSIZE, DEFDELTA, FALSE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(av->dim, numdimensions);
+ CU_ASSERT_EQUAL(result = arrayvalue_count(av), dim1count);
+
+ /* assert that arrayvalue content matches test data */
+ for(i = 0; i < dim1count; i++)
+ {
+ subav = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(subav);
+ CU_ASSERT_EQUAL(((etch_object*)subav)->obj_type, ETCHTYPEB_ARRAYVAL);
+ CU_ASSERT_EQUAL(result = arrayvalue_count(subav), dim0count);
+
+ for(j = 0; j < dim0count; j++)
+ {
+ wchar_t* expected_value = x[i][j];
+ thisitem = arrayvalue_get(subav, j);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_STRING);
+ result = wcscmp(expected_value, thisitem->v.value);
+ CU_ASSERT_EQUAL(result, 0);
+ }
+ }
+
+ /* destroy array value and content. memory includes the sub-arrayvalues,
+ * the native array from above, and the sub-native arrays */
+ etch_object_destroy(av);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_create_from_13
+ * create arrayvalue from two-dimensioned object nativearray and verify
+ */
+static void test_create_from_13(void)
+{
+ etch_nativearray* a = NULL;
+ etch_arrayvalue* av = NULL;
+ etch_arrayvalue* subav = NULL;
+ etch_object* thisitem = NULL;
+ const int DEFSIZE=0, DEFDELTA=0;
+ int i = 0, j = 0, result = 0, count = 0;
+ byte fake_typecode = 0xff;
+
+ etch_object* x[2][3] = { { NULL, NULL, NULL, }, { NULL, NULL, NULL, }, };
+
+ const int numdimensions = 2, dim0count = 3, dim1count = 2;
+
+ for(i = 0; i < dim1count; i++) /* populate array of objects */
+ for(j = 0; j < dim0count; j++)
+ { etch_object* thisobj = new_object(sizeof(etch_object), ETCHTYPEB_ETCHOBJECT,CLASSID_NONE);
+ thisobj->hashkey = ++count; /* usurp hashkey for this test */
+ x[i][j] = thisobj;
+ }
+
+ /* create an etch_nativearray from the above array of objects.
+ * native array owns its data and so since the data are etch objects
+ * the native array will destroy them at such time as arrayvalue
+ * destroys the native array, the objects likewise destroying their
+ * own content (that content being the etch_malloc'ed memory, above).
+ */
+ a = new_etch_nativearray_from(&x, CLASSID_ARRAY_OBJECT,
+ sizeof(void*), numdimensions, dim0count, dim1count, 0);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_ETCHOBJECT; /* array content is type object */
+ a->content_class_id = CLASSID_OBJECT; /* array content is wrapped */
+
+ for(i = 0; i < dim1count; i++) /* assert that native array content */
+ for(j = 0; j < dim0count; j++) /* when read back, is as expected */
+ { etch_object* thisx = 0, *thatx = x[i][j];
+ result = a->get2(a, &thisx, i, j);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisx);
+ CU_ASSERT_PTR_EQUAL_FATAL(thisx, thatx);
+ }
+
+ /* populate arrayvalue from native array. we tell the arrayvalue to take
+ * ownership of the native array. result is a 2-dimensional arrayvalue,
+ * which is represented as an arrayvalue of arrayvalues
+ */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, DEFSIZE, DEFDELTA, FALSE);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(av->dim, numdimensions);
+ CU_ASSERT_EQUAL(result = arrayvalue_count(av), dim1count);
+
+ /* assert that arrayvalue content matches test data */
+ for(i = 0; i < dim1count; i++)
+ {
+ subav = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(subav);
+ CU_ASSERT_EQUAL(((etch_object*)subav)->obj_type, ETCHTYPEB_ARRAYVAL);
+ CU_ASSERT_EQUAL(result = arrayvalue_count(subav), dim0count);
+
+ for(j = 0; j < dim0count; j++)
+ {
+ etch_object* expected_object = x[i][j];
+ thisitem = arrayvalue_get(subav, j);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL_FATAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_ETCHOBJECT);
+ // TODO this is CLASSID_ANY (zero) not CLASSID_OBJECT - verify that this is as expected
+ // CU_ASSERT_EQUAL_FATAL(((etch_object*)thisitem)->class_id, CLASSID_OBJECT);
+ CU_ASSERT_EQUAL(thisitem->hashkey, expected_object->hashkey);
+ }
+ }
+
+ /* destroy array value and content. memory freed includes sub-arrayvalues,
+ * the native array and test objects from above, and the sub-arrays */
+ etch_object_destroy(av);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_create_from_21
+ * create arrayvalue from three-dimensioned byte nativearray and verify
+ */
+static void test_create_from_21(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_arrayvalue* subav1 = NULL;
+ etch_arrayvalue* subav2 = NULL;
+ etch_byte* thisitem = NULL;
+ int i = 0, j = 0, k = 0, result = 0;
+ const int DEFSIZE=0, DEFDELTA=0;
+ byte fake_typecode = 0xff;
+
+ char x[2][3][4]
+ = { { {'a','b','c','d',},
+ {'e','f','g','h',},
+ {'i','j','k','l',},
+ },
+ { {'m','n','o','p',},
+ {'q','r','s','t',},
+ {'u','v','w','x',},
+ },
+ };
+
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+
+ #if(0)
+ etch_nativearray* a = new_etch_nativearray /* construct empty native array */
+ (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, dim0count, dim1count, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_BYTE; /* array content is type byte */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+
+ for(i = 0; i < dim2count; i++) /* populate native array iteratively */
+ for(j = 0; j < dim1count; j++)
+ for(k = 0; k < dim0count; k++)
+ CU_ASSERT_EQUAL(0, result = a->put3(a, &x[i][j][k], i, j, k));
+ #else
+ /* populate native arrary from static test data array */
+
+ etch_nativearray* a = new_etch_nativearray_from(&x, CLASSID_ARRAY_BYTE,
+ sizeof(byte), numdimensions, dim0count, dim1count, dim2count);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+ a->content_obj_type = ETCHTYPEB_BYTE; /* array content is type byte */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped */
+
+ #endif
+
+ /* populate arrayvalue from native array. we tell the arrayvalue to take
+ * ownership of the native array. result is a 3-dimensional arrayvalue,
+ * which is represented as an arrayvalue of arrayvalues of arrayvalues.
+ */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, DEFSIZE, DEFDELTA, FALSE);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(av->dim, numdimensions);
+ CU_ASSERT_EQUAL(arrayvalue_count(av), dim2count);
+
+ /* assert that arrayvalue content matches test data */
+ for(i = 0; i < dim2count; i++)
+ {
+ subav1 = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(subav1);
+ CU_ASSERT_EQUAL(((etch_object*)subav1)->obj_type, ETCHTYPEB_ARRAYVAL);
+ CU_ASSERT_EQUAL(result = arrayvalue_count(subav1), dim1count);
+
+ for(j = 0; j < dim1count; j++)
+ {
+ subav2 = arrayvalue_get(subav1, j);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(subav2);
+ CU_ASSERT_EQUAL(((etch_object*)subav2)->obj_type, ETCHTYPEB_ARRAYVAL);
+ CU_ASSERT_EQUAL(result = arrayvalue_count(subav2), dim0count);
+
+ for(k = 0; k < dim0count; k++)
+ {
+ byte expected_value = x[i][j][k];
+ thisitem = arrayvalue_get(subav2, k);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_PRIMITIVE_BYTE);
+ CU_ASSERT_EQUAL(((etch_byte*)thisitem)->value, expected_value);
+ }
+ }
+ }
+
+ /* destroy array value and content. memory freed includes the native array
+ * constructed above, the arrayvalue, its sub-arrayvalues and their sub-
+ * native arrays, and any disposable native array content.
+ */
+ etch_object_destroy(av);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_arrayvalue_to_nativearray_10
+ * create create native byte[] array from arrayvalue
+ */
+static void test_arrayvalue_to_nativearray_10(void)
+{
+ etch_arrayvalue* av = NULL;
+ int i = 0, result = 0;
+ byte fake_typecode = 0xff;
+ char x[4] = {'a','b','c','d'};
+ const int numdimensions = 1, itemcount = 4;
+ etch_nativearray* old_nativearray_dangling_ref = NULL;
+
+ etch_nativearray* a = new_etch_nativearray(CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, itemcount, 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+
+ a->content_obj_type = ETCHTYPEB_BYTE; /* array content is type byte */
+ a->content_class_id = CLASSID_NONE; /* array content is unwrapped (default) */
+
+ for(i = 0; i < itemcount; i++) /* populate native array from test data */
+ CU_ASSERT_EQUAL(0, result = a->put1(a, &x[i], i));
+
+ /* populate arrayvalue from native array */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, itemcount, 0, FALSE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+ CU_ASSERT_EQUAL(arrayvalue_count(av), itemcount);
+
+ for(i=0; i < itemcount; i++)
+ {
+ etch_byte* thisitem = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(thisitem->value, x[i]);
+ }
+
+ /* this will be a dangling pointer after we arrayvalue_to_nativearray() */
+ old_nativearray_dangling_ref = av->natarray;
+
+ /* convert new arrayvalue back to a nativearray. the embedded nativearray
+ * we created above, is destroyed in the process, and replaced with a new
+ * nativearray, whose content should of course be identical to the old.
+ */
+ result = arrayvalue_to_nativearray(av);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (av->natarray);
+ CU_ASSERT_PTR_NOT_EQUAL_FATAL(av->natarray, old_nativearray_dangling_ref);
+ CU_ASSERT_EQUAL_FATAL(result,itemcount); /* returns items inserted count */
+
+ /* get items one at a time from the arrayvalue's new native array,
+ * and compare to expected value from the test data above.
+ */
+ for(i = 0; i < itemcount; i++)
+ {
+ byte c2=0, c1 = x[i];
+ result = av->natarray->get1(av->natarray, &c2, i);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(c1, c2);
+ }
+
+ etch_object_destroy(av); /* destroy array value and content */
+}
+
+
+/*
+ * test_arrayvalue_to_nativearray_20
+ * create create native string[][] array from arrayvalue
+ */
+static void test_arrayvalue_to_nativearray_20(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_arrayvalue* subav = NULL;
+ etch_string* thisitem = NULL;
+ byte fake_typecode = 0xff;
+ const int numdimensions = 2, dim0count = 4, dim1count = 6;
+ const int DEFSIZE=0, DEFDELTA=0;
+ int i = 0, j = 0, result = 0;
+ etch_nativearray* old_nativearray_dangling_ref = NULL;
+
+ wchar_t* x[6][4]
+ = { { L"I think that I", L"shall never see,", L"A poem as lovely", L"as a tree.", },
+ { L"She knows not what", L"the curse may be,", L"And so she weaveth", L"steadily,", },
+ { L"And little other", L"care hath she,", L"The Lady of Shalott", L".", },
+ { L"And therefore I have sailed", L"the seas, and come", L"To the holy city", L"of Byzantium.", },
+ { L"Once out of nature", L"I shall never take", L"My bodily form", L" from any natural thing,", },
+ { L"But such a form", L"as Grecian goldsmiths make", L"Of hammered gold ", L"and gold enameling", },
+ };
+
+ etch_nativearray* a = new_etch_nativearray_from(&x, CLASSID_ARRAY_STRING,
+ sizeof(void*), numdimensions, dim0count, dim1count, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+ a->content_obj_type = ETCHTYPEB_STRING; /* content is type string */
+ a->content_class_id = CLASSID_UNWRAPPED; /* content is unwrapped (default) */
+
+ /* populate arrayvalue from native array as in earlier test */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, DEFSIZE, DEFDELTA, FALSE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+
+ /* this will be a dangling pointer after we arrayvalue_to_nativearray() */
+ old_nativearray_dangling_ref = av->natarray;
+
+ /* convert new arrayvalue back to a nativearray. the embedded nativearray
+ * we created above, is destroyed in the process, and replaced with a new
+ * nativearray, whose content should of course be identical to the old.
+ */
+ result = arrayvalue_to_nativearray(av);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (av->natarray);
+ CU_ASSERT_PTR_NOT_EQUAL_FATAL(av->natarray, old_nativearray_dangling_ref);
+ CU_ASSERT_EQUAL_FATAL(result, dim0count * dim1count);
+
+ /* get items one at a time from the arrayvalue's new native array,
+ * and compare to expected value from the test data above.
+ */
+ for(i = 0; i < dim1count; i++)
+ {
+ subav = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(subav);
+ CU_ASSERT_EQUAL(((etch_object*)subav)->obj_type, ETCHTYPEB_ARRAYVAL);
+ CU_ASSERT_EQUAL(result = arrayvalue_count(subav), dim0count);
+
+ for(j = 0; j < dim0count; j++)
+ {
+ wchar_t* expected_value = x[i][j];
+ thisitem = arrayvalue_get(subav, j);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_STRING);
+ result = wcscmp(expected_value, thisitem->v.value);
+ CU_ASSERT_EQUAL(result, 0);
+ }
+ }
+
+ etch_object_destroy(av); /* destroy array value and content */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_arrayvalue_to_nativearray_30
+ * create create native byte[][][] array from arrayvalue
+ */
+static void test_arrayvalue_to_nativearray_30(void)
+{
+ etch_arrayvalue* av = NULL;
+ etch_arrayvalue* subav1 = NULL;
+ etch_arrayvalue* subav2 = NULL;
+ etch_byte* thisitem = NULL;
+ byte fake_typecode = 0xff;
+ int i = 0, j = 0, k = 0, result = 0;
+ const int DEFSIZE=0, DEFDELTA=0;
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+ etch_nativearray* old_nativearray_dangling_ref = NULL;
+
+ char x[2][3][4]
+ = { { {'a','b','c','d',},
+ {'e','f','g','h',},
+ {'i','j','k','l',},
+ },
+ { {'m','n','o','p',},
+ {'q','r','s','t',},
+ {'u','v','w','x',},
+ },
+ };
+
+ etch_nativearray* a = new_etch_nativearray_from(&x, CLASSID_ARRAY_BYTE,
+ sizeof(byte), numdimensions, dim0count, dim1count, dim2count);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(a);
+ a->content_obj_type = ETCHTYPEB_BYTE; /* content is type byte */
+ a->content_class_id = CLASSID_NONE; /* content is unwrapped */
+
+ /* populate arrayvalue from native array. we tell the arrayvalue to take
+ * ownership of the native array. result is a 3-dimensional arrayvalue,
+ * which is represented as an arrayvalue of arrayvalues of arrayvalues.
+ */
+ av = new_arrayvalue_from(a, fake_typecode, NULL, DEFSIZE, DEFDELTA, FALSE);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(av);
+
+ /* this will be a dangling pointer after we arrayvalue_to_nativearray() */
+ old_nativearray_dangling_ref = av->natarray;
+
+ /* convert new arrayvalue back to a nativearray. the embedded nativearray
+ * we created above, is destroyed in the process, and replaced with a new
+ * nativearray, whose content should of course be identical to the old.
+ */
+ result = arrayvalue_to_nativearray(av);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (av->natarray);
+ CU_ASSERT_PTR_NOT_EQUAL_FATAL(av->natarray, old_nativearray_dangling_ref);
+ CU_ASSERT_EQUAL_FATAL(result, dim0count * dim1count * dim2count);
+
+ /* get items one at a time from the arrayvalue's new native array,
+ * and compare to expected value from the test data above.
+ */
+ for(i = 0; i < dim2count; i++)
+ {
+ subav1 = arrayvalue_get(av, i);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(subav1);
+ CU_ASSERT_EQUAL(((etch_object*)subav1)->obj_type, ETCHTYPEB_ARRAYVAL);
+ CU_ASSERT_EQUAL(result = arrayvalue_count(subav1), dim1count);
+
+ for(j = 0; j < dim1count; j++)
+ {
+ subav2 = arrayvalue_get(subav1, j);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(subav2);
+ CU_ASSERT_EQUAL(((etch_object*)subav2)->obj_type, ETCHTYPEB_ARRAYVAL);
+ CU_ASSERT_EQUAL(result = arrayvalue_count(subav2), dim0count);
+
+ for(k = 0; k < dim0count; k++)
+ {
+ byte expected_value = x[i][j][k];
+ thisitem = arrayvalue_get(subav2, k);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thisitem);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->obj_type, ETCHTYPEB_PRIMITIVE);
+ CU_ASSERT_EQUAL(((etch_object*)thisitem)->class_id, CLASSID_PRIMITIVE_BYTE);
+ CU_ASSERT_EQUAL(((etch_byte*)thisitem)->value, expected_value);
+ }
+ }
+ }
+
+ etch_object_destroy(av); /* destroy array value and content */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_arrayvalue_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("suite_arrayvalue", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test iterator over arraylist", test_iterator_over_arraylist);
+ #if(0)
+ CU_add_test(pSuite, "test array value read ints", read_test_integer);
+ CU_add_test(pSuite, "test array value read strings", read_test_string);
+ CU_add_test(pSuite, "test array value write ints", write_test_integer);
+ CU_add_test(pSuite, "test array value write strings", write_test_string);
+ #endif
+ CU_add_test(pSuite, "test predefined exception", exception_test_1);
+ CU_add_test(pSuite, "test copy text exception", exception_test_2);
+ CU_add_test(pSuite, "test global text exception", exception_test_3);
+ CU_add_test(pSuite, "test create from byte[]", test_create_from_1);
+ CU_add_test(pSuite, "test create from int[]", test_create_from_2);
+ CU_add_test(pSuite, "test create from int64[]", test_create_from_3);
+ CU_add_test(pSuite, "test create from double[]", test_create_from_4);
+ CU_add_test(pSuite, "test create from string[]", test_create_from_5);
+ CU_add_test(pSuite, "test create from byte[][]", test_create_from_10);
+ CU_add_test(pSuite, "test create from short[][]", test_create_from_11);
+ CU_add_test(pSuite, "test create from string[][]", test_create_from_12);
+ CU_add_test(pSuite, "test create from object[][]", test_create_from_13);
+ CU_add_test(pSuite, "test create from byte[][][]", test_create_from_21);
+ CU_add_test(pSuite, "test arrayvalue to byte[]", test_arrayvalue_to_nativearray_10);
+ CU_add_test(pSuite, "test arrayvalue to string[][]", test_arrayvalue_to_nativearray_20);
+ CU_add_test(pSuite, "test arrayvalue to byte[][][]", test_arrayvalue_to_nativearray_30);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/message/test_binarytditdo.c b/binding-c/runtime/c/src/test/message/test_binarytditdo.c
new file mode 100644
index 0000000..352966e
--- /dev/null
+++ b/binding-c/runtime/c/src/test/message/test_binarytditdo.c
@@ -0,0 +1,2457 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_binarytditdo.c
+ * test binary input and output
+ */
+
+#include "etch_runtime.h"
+#include "etch_binary_tdi.h"
+#include "etch_binary_tdo.h"
+#include "etch_connection.h"
+#include "etch_default_value_factory.h"
+#include "etch_binary_tdo.h"
+#include "etch_binary_tdi.h"
+#include "etch_messagizer.h"
+#include "etch_resources.h"
+#include "etch_nativearray.h"
+#include "etch_arrayval.h"
+#include "etch_thread.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_map.h"
+#include "etch_log.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - -
+ */
+
+static etch_type* mt_footype;
+static etch_field* mf_fookey;
+static int objcount, arraycount;
+static unsigned short CLASSID_MY_IMPL_TP;
+
+
+static void this_test_setup()
+{
+ mt_footype = new_static_type(L"footype");
+ mf_fookey = new_static_field(L"foofield");
+}
+
+static void this_test_teardown()
+{
+ destroy_static_type(mt_footype);
+ destroy_static_field(mf_fookey);
+}
+
+#if 0
+/**
+ * etch_system_nanotime()
+ * operating system specific implementation of java System.nanotime().
+ */
+static int64 etch_system_nanotime()
+{
+ int64 result = 0;
+
+#ifdef WIN32
+ LARGE_INTEGER li;
+ QueryPerformanceCounter(&li);
+ result = li.QuadPart;
+#else
+ #warning etch_system_nanotime have to be checked, we need a nanotime function
+ //TODO check this
+ struct timeval time;
+ gettimeofday(&time, NULL);
+ result = time.tv_usec * 1000;
+#endif
+
+ return result;
+}
+#endif
+
+static signed char* get_test_bytes(signed char minval, int n)
+{
+ int i = 0;
+ signed char* vals = malloc(n * sizeof(char));
+ while(n--) vals[i++] = minval++;
+ return vals;
+}
+
+
+static short* get_test_shorts(short minval, int n)
+{
+ int i = 0;
+ short* vals = malloc(n * sizeof(short));
+ while(n--) vals[i++] = minval++;
+ return vals;
+}
+
+
+static int* get_test_ints(int* out)
+{
+ int n = 65536+2+2, k = 65536+2, i = 0;
+ int minval = ETCHTYPE_MIN_INT16 - 1;
+ int* vals = malloc(n * sizeof(int));
+ while(k--) vals[i++] = minval++;
+ vals[i++] = ETCHTYPE_MIN_INT32;
+ vals[i++] = ETCHTYPE_MAX_INT32;
+ *out = n;
+ return vals;
+}
+
+
+static int64* get_test_longs(int* out)
+{
+ int n = 65536+2+6, k = 65536+2, i = 0;
+ int minval = ETCHTYPE_MIN_INT16 - 1;
+ int64* vals = malloc(n * sizeof(int64));
+ while(k--) vals[i++] = minval++;
+ vals[i++] = ETCHTYPE_MIN_INT32;
+ vals[i++] = ETCHTYPE_MAX_INT32;
+ vals[i++] = (int64) ETCHTYPE_MIN_INT32 - 1;
+ vals[i++] = (int64) ETCHTYPE_MAX_INT32 + 1;
+ vals[i++] = ETCHTYPE_MIN_INT64;
+ vals[i++] = ETCHTYPE_MAX_INT64;
+ *out = n;
+ return vals;
+}
+
+
+#if 0
+/**
+ * do_perftest
+ * measure performance of messagizer.transport_message()
+ */
+static void do_perftest(char* name, const int iter, etch_messagizer* messagizer,
+ etch_message* msg, const int n)
+{
+ int64 t0, t1;
+ double dtime, drate;
+ int i = 0, result = 0;
+ etch_who* whofrom = NULL;
+
+ /* ensure message will not be destroyed by transport_message
+ * since we invoke transport_message multiple times here */
+ unsigned char save_msg_staticflag = ((etch_object*)msg)->is_static;
+ set_etchobj_static_all(msg);
+ printf("\n");
+
+ t0 = etch_system_nanotime();
+
+ for(; i < n; i++)
+ result = messagizer->transport_message(messagizer, whofrom, msg);
+
+ t1 = etch_system_nanotime();
+
+ dtime = (t1 - t0) / (double) 1000000000;
+ drate = (double) n / dtime;
+
+ printf("%s iteration %d time %7.3f count %d rate %7.3f\n", name, iter, dtime, n, drate);
+
+ ((etch_object*)msg)->is_static = save_msg_staticflag;
+}
+#endif
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * my_impl_transportpacket (i_transportpacket implementation)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * my_impl_transportpacket
+ * test object implementing i_transportpacket
+ */
+typedef struct my_impl_transportpacket
+{
+ etch_object object;
+
+ /* i_transportpacket interface and methods, plus original destructor
+ * which becomes replaced with a custom destructor to destroy this
+ * object. this is the model for destroying an interface wrapper object
+ * when we do not save and pass around a pointer to the wrapper, but rather
+ * a pointer to the interface. the interface in question, i_transportpacket
+ * in this case, contains a pointer to the wrapper object, in this case a
+ * my_impl_transportpacket*. when the interface is instantiated, its original
+ * destructor is saved, and is replaced with a destructor which invokes
+ * the wrapper's destructor. the wrapper destructor must then know to
+ * invoke the interface's original destructor when destroying the interface.
+ */
+ i_transportpacket* ixp; /* owned */
+ etch_object_destructor destroy_transportpacket; /* i_transportpacket original destructor */
+ etch_transport_packet transport_packet; /* transport_packet() */
+ etch_transport_packet_headersize header_size; /* header_size() */
+
+ i_sessionpacket* session; /* not owned */
+
+} my_impl_transportpacket;
+
+
+/**
+ * destroy_my_impl_transportpacket()
+ * my_impl_transportpacket destructor
+ */
+static int destroy_my_impl_transportpacket(void* transport_package)
+{
+ my_impl_transportpacket* thisx = (my_impl_transportpacket*)transport_package;
+
+ if (!is_etchobj_static_content(thisx))
+ { /* invoke original i_transportpacket destructor */
+ if (thisx->ixp && thisx->destroy_transportpacket)
+ thisx->destroy_transportpacket(thisx->ixp);
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * impl_transport_packet()
+ * my_impl_transportpacket::transport_packet
+ * @param whoto caller retains, can be null
+ * @param fbuf caller retains
+ */
+static int impl_transport_packet (void* data, void* whoto, void* fbuf)
+{
+ return 0;
+}
+
+
+/**
+ * my_transport_control()
+ * my_impl_transportpacket::itransport::transport_control
+ */
+static int my_transport_control (void* data, etch_object* control, etch_object* value)
+{
+ return 0;
+}
+
+
+/**
+ * my_transport_notify()
+ * my_impl_transportpacket::itransport::transport_notify
+ */
+static int my_transport_notify (i_transportpacket* itp, etch_object* evt)
+{
+ return 0;
+}
+
+
+/**
+ * my_transport_query()
+ * my_impl_transportpacket::itransport::transport_query
+ */
+static etch_object* my_transport_query (i_transportpacket* itp, etch_object* query)
+{
+ return NULL;
+}
+
+
+/**
+ * my_transport_get_session()
+ * my_impl_transportpacket::itransport::get_session
+ */
+static i_sessionpacket* my_transport_get_session(i_transportpacket* itp)
+{
+ my_impl_transportpacket* mytp = itp->thisx;
+ return mytp->session;
+}
+
+
+/**
+ * my_transport_set_session()
+ * my_impl_transportpacket::itransport::set_session
+ */
+static void my_transport_set_session(i_transportpacket* itp, i_sessionpacket* session)
+{
+ my_impl_transportpacket* mytp = itp->thisx;
+ mytp->session = session;
+}
+
+
+/*
+ * destroy_my_transportpacket()
+ * i_transportpacket destructor
+ * this destructor will destroy its parent (my_impl_transportpacket),
+ * which will in turn destroy this object.
+ */
+static int destroy_my_transportpacket(void* data)
+{
+ i_transportpacket* itp = (i_transportpacket*)data;
+ my_impl_transportpacket* mytp = NULL;
+ if (NULL == itp) return -1;
+
+ mytp = itp->thisx;
+
+ etch_object_destroy(mytp);
+
+ return 0;
+}
+
+/**
+ * new_my_impl_transportpacket()
+ * my_impl_transportpacket constructor
+ */
+static my_impl_transportpacket* new_my_impl_transportpacket()
+{
+ i_transportpacket* itp = NULL;
+ i_transport* itransport = NULL;
+ /* this is a model for dynamic class ID assigment */
+ unsigned short class_id = CLASSID_MY_IMPL_TP? CLASSID_MY_IMPL_TP:
+ (CLASSID_MY_IMPL_TP = get_dynamic_classid());
+
+ my_impl_transportpacket* mytp = (my_impl_transportpacket*) new_object
+ (sizeof(my_impl_transportpacket), ETCHTYPEB_TRANSPORTPKT, class_id);
+
+ ((etch_object*)mytp)->destroy = destroy_my_impl_transportpacket;
+
+ itransport = new_transport_interface_ex(mytp,
+ (etch_transport_control) my_transport_control,
+ (etch_transport_notify) my_transport_notify,
+ (etch_transport_query) my_transport_query,
+ (etch_transport_get_session) my_transport_get_session,
+ (etch_transport_set_session) my_transport_set_session);
+
+ itp = new_transportpkt_interface(mytp, impl_transport_packet, itransport);
+
+ /* save off i_transportpacket destructor */
+ mytp->destroy_transportpacket = ((etch_object*)itp)->destroy;
+
+ /* replace i_transportpacket destructor with one which will destroy this object */
+ ((etch_object*)itp)->destroy = destroy_my_transportpacket;
+
+ /* g_my_transportpacket will get set to this interface */
+ mytp->ixp = itp;
+
+ return mytp;
+}
+
+
+
+/* - - - - - - - - - - - - - -
+ * unit tests
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_constructor_0
+ * verify that memory is freed as expected
+ */
+static void test_constructor_0(void)
+{
+ binary_tagged_data_output* tdo = new_binary_tagdata_output(NULL, NULL);
+
+ etch_object_destroy(tdo);
+
+ etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_constructor_1
+ * verify that memory is freed as expected
+ */
+static void test_constructor_1(void)
+{
+
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ binary_tagged_data_output* tdo = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ tdo = new_binary_tagdata_output((etch_value_factory*) vf, NULL);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ etch_object_destroy(tdo);
+
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_constructor_2
+ * verify that memory is freed as expected
+ */
+static void test_constructor_2(void)
+{
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ binary_tagged_data_output* tdo = NULL;
+ etch_flexbuffer* fbuf = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ fbuf = new_flexbuffer(0);
+ CU_ASSERT_PTR_NOT_NULL(fbuf);
+
+ /* since we pass in vf and buf, we retain ownership */
+ tdo = new_binary_tagdata_output((etch_value_factory*)vf, fbuf);
+ CU_ASSERT_PTR_NOT_NULL(tdo);
+
+ etch_object_destroy(fbuf);
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ etch_object_destroy(tdo);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_check_value()
+ */
+static void test_check_value(void)
+{
+ int i = 0;
+ signed char typecode = 0;
+ binary_tagged_data_output* tdo = new_binary_tagdata_output(NULL, NULL);
+
+ do /* check byte values */
+ { signed char *testbytes = get_test_bytes(ETCHTYPE_MIN_BYTE, 256), c = 0;
+ etch_byte* byteobj = new_byte(0);
+
+ for(i = 0; i < 256; i++)
+ {
+ byteobj->value = c = testbytes[i];
+ typecode = etchtagdata_check_value((etch_object*) byteobj);
+
+ if (is_inrange_tiny_for_signed_chars(c))
+ { CU_ASSERT_EQUAL(typecode, c); }
+ else
+ { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTE); }
+ }
+
+ free(testbytes);
+ etch_object_destroy(byteobj);
+
+ } while(0);
+
+ do /* check short values */
+ { short *testshorts = get_test_shorts(ETCHTYPE_MIN_INT16, 65536), n = 0;
+ etch_int16* shortobj = new_int16(0);
+
+ for(i = 0; i < 65536; i++)
+ {
+ shortobj->value = n = testshorts[i];
+ typecode = etchtagdata_check_value((etch_object*) shortobj);
+
+ if (is_inrange_tiny(n))
+ { CU_ASSERT_EQUAL(typecode, (signed char) n); }
+ else
+ if (is_inrange_byte(n))
+ { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTE); }
+ else
+ { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_SHORT); }
+ }
+
+ free(testshorts);
+ etch_object_destroy(shortobj);
+
+ } while(0);
+
+ do /* check int values */
+ { int intcount = 0, *testints = get_test_ints(&intcount), n = 0;
+ etch_int32* intobj = new_int32(0);
+
+ for(i = 0; i < intcount; i++)
+ {
+ intobj->value = n = testints[i];
+ typecode = etchtagdata_check_value((etch_object*) intobj);
+
+ if (is_inrange_tiny(n))
+ { CU_ASSERT_EQUAL(typecode, (signed char) n); }
+ else
+ if (is_inrange_byte(n))
+ { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTE); }
+ else
+ if (is_inrange_int16(n))
+ { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_SHORT); }
+ else
+ { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_INT); }
+ }
+
+ free(testints);
+ etch_object_destroy(intobj);
+
+ } while(0);
+
+ do /* check long values */
+ { int longcount = 0;
+ int64 *testlongs = get_test_longs(&longcount), n = 0;
+ etch_int64* longobj = new_int64(0);
+
+ for(i = 0; i < longcount; i++)
+ {
+ longobj->value = n = testlongs[i];
+ typecode = etchtagdata_check_value((etch_object*) longobj);
+
+ if (is_inrange_tiny(n))
+ { CU_ASSERT_EQUAL(typecode, (signed char) n); }
+ else
+ if (is_inrange_byte(n))
+ { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTE); }
+ else
+ if (is_inrange_int16(n))
+ { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_SHORT); }
+ else
+ if (is_inrange_int32(n))
+ { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_INT); }
+ else
+ { CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_LONG); }
+ }
+
+ free(testlongs);
+ etch_object_destroy(longobj);
+
+ } while(0);
+
+ typecode = etchtagdata_check_value(NULL);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_NULL);
+
+ do { /* check boolean values */
+ etch_boolean* boolobj = new_boolean(FALSE);
+ typecode = etchtagdata_check_value((etch_object*)boolobj);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE);
+ boolobj->value = TRUE;
+ typecode = etchtagdata_check_value((etch_object*)boolobj);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE);
+ etch_object_destroy(boolobj);
+ } while(0);
+
+ do { /* check ieee values */
+ etch_float* floatobj = new_float((float) 3.14159);
+ etch_double* doubobj = new_double(3.14159);
+ typecode = etchtagdata_check_value((etch_object*)floatobj);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_FLOAT);
+ typecode = etchtagdata_check_value((etch_object*)doubobj);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_DOUBLE);
+ etch_object_destroy(floatobj);
+ etch_object_destroy(doubobj);
+ } while(0);
+
+ do { /* check bytes */
+ etch_nativearray* bytearray = new_etch_nativearray(CLASSID_ARRAY_BYTE, sizeof(byte), 1, 4, 0, 0);
+ etch_arrayvalue* arrayvalu = new_arrayvalue(ETCH_XTRNL_TYPECODE_BYTES, NULL, 1, 0, 0, 0, 0);
+ arrayvalu->content_obj_type = ETCHTYPEB_BYTE;
+ typecode = etchtagdata_check_value((etch_object*)bytearray);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTES);
+ typecode = etchtagdata_check_value((etch_object*)arrayvalu);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_BYTES);
+ etch_object_destroy(bytearray);
+ etch_object_destroy(arrayvalu);
+ } while(0);
+
+ do { /* check string */
+ etch_string* strempty = new_stringw(L"");
+ etch_string* strother = new_stringw(L"x");
+ typecode = etchtagdata_check_value((etch_object*)strempty);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_EMPTY_STRING);
+ typecode = etchtagdata_check_value((etch_object*)strother);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_STRING);
+ etch_object_destroy(strempty);
+ etch_object_destroy(strother);
+ } while(0);
+
+ do { /* check custom */
+ etch_type* newtype = new_type(L"unknown");
+ etch_date* newdate = new_date();
+ etch_structvalue* sv = new_structvalue(newtype, 0);
+ typecode = etchtagdata_check_value((etch_object*)sv);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_CUSTOM);
+ typecode = etchtagdata_check_value((etch_object*)newdate);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_CUSTOM);
+ etch_object_destroy(newdate);
+ etch_object_destroy(newtype);
+ etch_object_destroy(sv);
+ } while(0);
+
+ do { /* check end of data marker */
+ etch_object* eodobj = etchtagdata_new_eodmarker(FALSE);
+ typecode = etchtagdata_check_value((etch_object*)eodobj);
+ CU_ASSERT_EQUAL(typecode, (signed char) ETCH_XTRNL_TYPECODE_NONE);
+ etch_object_destroy(eodobj);
+ } while(0);
+
+ etch_object_destroy(tdo);
+
+ etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * message_to_bytes()
+ * return the encoded bytes for a message.
+ * caller relinquishes msg, assumes returned bytes.
+ */
+static byte* message_to_bytes(default_value_factory* vf, etch_message* msg, size_t* out)
+{
+ size_t bufcount = 0;
+ byte* msgbytes = NULL;
+
+ etch_flexbuffer* fbuf = new_flexbuffer(ETCH_DEFSIZE);
+ binary_tagged_data_output* tdo = new_binary_tagdata_output(msg->vf, fbuf);
+
+ if (0 == ((struct i_tagged_data_output*)((etch_object*)tdo)->vtab)->write_message((tagged_data_output*) tdo, msg, fbuf))
+ {
+ etch_flexbuf_set_index(fbuf, 0);
+ msgbytes = etch_flexbuf_get_all(fbuf, &bufcount);
+ }
+
+ etch_object_destroy(fbuf);
+ etch_object_destroy(tdo);
+ etch_object_destroy(msg);
+
+ if (out) *out = bufcount;
+ return msgbytes;
+}
+
+
+/**
+ * bytes_to_message()
+ * return the message constructed from the supplied bytes.
+ * caller relinquishes bytes, assumes returned message.
+ */
+static etch_message* bytes_to_message(default_value_factory* vf, byte* bytes, const size_t bytecount)
+{
+ etch_flexbuffer* fbuf = new_flexbuffer_from(bytes, bytecount, bytecount, 0);
+
+ binary_tagged_data_input* tdi = new_binary_tagdata_input((etch_value_factory*) vf);
+
+ etch_message* msg = bintdi_read_message((tagged_data_input*) tdi, fbuf);
+
+ etch_object_destroy(tdi);
+ etch_object_destroy(fbuf); /* flexbuf destroys bytes */
+ return msg;
+}
+
+
+/**
+ * is_equal_objects()
+ * currently we don't have a convenient way to determine if objects are equal,
+ * primarily because the default hashkey is generated from object address, and
+ * we have to date omitted the equality function from out object definition.
+ * it remains to be seen whether we need a general equality test, in practice
+ * we may only need it for certain high level objects. so we'll wing it for now.
+ */
+static int is_equal_objects(etch_object* a, etch_object* b)
+{
+ if ((((etch_object*)a)->obj_type == b->obj_type) && (((etch_object*)a)->class_id == b->class_id))
+ return TRUE;
+
+ /* todo: tests for array type and content comparison here */
+
+ return FALSE;
+}
+
+
+/*
+ * show_value() - debug display for do_tdo_tdi_test()
+ */
+static void show_value(etch_object* obj)
+{
+ #if IS_DEBUG_CONSOLE
+
+ char x[2] = {0,0};
+
+ if (is_etch_byte(obj))
+ { x[0] = ((etch_byte*)obj)->value;
+ printf(" >>> '%s'\n", x);
+ }
+ else
+ if (is_etch_arrayvalue(obj))
+ { etch_arrayvalue* av = (etch_arrayvalue*) obj;
+ const int dim = av->dim;
+ const int count = av->list->count;
+ printf("$$$ array dim %d count %d\n", dim, count);
+ }
+ fflush(stdout);
+
+ #endif
+}
+
+
+/*
+ * do_tdo_tdi_test()
+*/
+static etch_object* do_tdo_tdi_test(default_value_factory* vf, etch_object* thisobj, etch_validator* vtor)
+{
+ etch_message* thismsg = NULL, *resultmsg = NULL;
+ etch_hashitem x, *thisitem = &x;
+ etch_object* returnobj = NULL;
+ byte* msgbytes = NULL;
+ size_t bytecount = 0;
+ int result = 0;
+ memset(thisitem, 0, sizeof(thisitem));
+ show_value(thisobj);
+
+ etchtype_clear_validator(mt_footype, mf_fookey);
+ result = etchtype_put_validator (mt_footype, clone_field(mf_fookey), (etch_object*) vtor);
+ CU_ASSERT_EQUAL(result, 0);
+ /* protect caller's object so that temporary owners in this test can't destroy it */
+ set_etchobj_static_all(thisobj);
+
+ thismsg = new_message(mt_footype, ETCH_DEFSIZE, (etch_value_factory*) vf);
+
+ result = message_put(thismsg, clone_field(mf_fookey), thisobj);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ /* serialize thismsg. relinquish thismsg, assume msgbytes */
+ msgbytes = message_to_bytes(vf, thismsg, &bytecount);
+ CU_ASSERT_PTR_NOT_NULL(msgbytes);
+ CU_ASSERT_NOT_EQUAL(bytecount, 0);
+
+ /* deserialize msgbytes. relinquish msgbytes, assume resultmsg */
+ resultmsg = bytes_to_message(vf, msgbytes, bytecount);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(resultmsg);
+
+ result = structvalue_is_type(resultmsg->sv, mt_footype);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ result = message_size(resultmsg);
+ CU_ASSERT_EQUAL(result, 1);
+
+ result = etchmap_map_find(resultmsg->sv->items, (etch_object*) mf_fookey, &thisitem);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* we verified that the deserialized message contains caller's reconstituted object.
+ * we want to return that object to caller so that it can be compared with the
+ * original object; however before we return, we are going to destroy the message,
+ * and since the message owns its content, it will destroy the object we intend to
+ * return, so we remove the object from the message before we destroy the message.
+ * this call frees all hashtable memory associated with this key/value pair except
+ * except for the value object itself, including the key.
+ */
+ if (result == 0)
+ returnobj = message_remove(resultmsg, mf_fookey);
+
+ etch_object_destroy(resultmsg);
+ clear_etchobj_static_all(thisobj); /* unprotect caller's object protected above */
+ return returnobj;
+}
+
+
+/**
+ * do_recursive_test()
+ * recurse input object creating message from all content from scalar up to top level
+ * enclosing array. serialize and deserialize each message and verify that deserialized
+ * object matches original.
+ */
+static void do_recursive_test(default_value_factory* vf, etch_object* obj, etch_validator* vtor)
+{
+ etch_object* thisobj = obj, *reconstituted_obj = NULL;
+
+ if (is_etch_arrayvalue(thisobj))
+ {
+ etch_iterator iterator;
+ arrayvalue_set_iterator((etch_arrayvalue*) thisobj, &iterator);
+
+ while(iterator.has_next(&iterator))
+ {
+ etch_object* subobj = iterator.current_value;
+
+ do_recursive_test(vf, subobj, vtor->element_validator(vtor));
+
+ iterator.next(&iterator);
+ }
+ }
+ /* printf("\n(%05d. type %d class %d)\n", ++objcount, ((etch_object*)obj)->obj_type, ((etch_object*)obj)->class_id); fflush(stdout); */
+
+ reconstituted_obj = do_tdo_tdi_test(vf, thisobj, vtor);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(reconstituted_obj);
+ CU_ASSERT_EQUAL(is_equal_objects(thisobj, reconstituted_obj), TRUE);
+ etch_object_destroy(reconstituted_obj);
+}
+
+#ifdef _WIN32
+#pragma message ( "test_byte_array_out_in deactivated" )
+#else
+#warning "test_byte_array_out_in deactivated"
+#endif
+
+#if 0
+/**
+ * test_byte_array_out_in()
+ * test serialization and subsequent deserialization of a byte array
+ */
+static void test_byte_array_out_in(void)
+{
+ const int NUMDIMS = 2;
+ char x[2][3] = { {'a','b','c', }, {'d','e','f', }, };
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_nativearray* natarray = NULL;
+ etch_arrayvalue* arrayval = NULL;
+ etch_arrayvalue* reconstituted_arrayval = NULL;
+ etch_validator* validator = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ natarray = new_etch_nativearray_from(x, CLASSID_ARRAY_BYTE, sizeof(byte), NUMDIMS, 3, 2, 0);
+ arrayval = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_BYTES, NULL, 2, 0, FALSE);
+ validator = etchvtor_byte_get(NUMDIMS);
+
+ this_test_setup();
+ ((struct i_value_factory*)((etch_object*)vf)->vtab)->add_type(vf, mt_footype); /* the type we are testing with */
+ objcount = arraycount = 0; /* initialize global counters */
+
+ /* test serialization and deserialization of the array object as a whole.
+ * we are returned an object which is the reconstituted array after it has
+ * been serialized and subsequently deserialized */
+ reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test
+ (vf, (etch_object*) arrayval, validator);
+
+ CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);
+
+ /* recursively test serialization and deserialization of all the components
+ * of the array object, including the array itself, subarrays, and scalars,
+ * as per the java version of this test. this really ensures that we have
+ * all our memory management ducks in a row. */
+
+ do_recursive_test (vf, (etch_object*) arrayval, validator);
+
+ etch_object_destroy(arrayval);
+ etch_object_destroy(reconstituted_arrayval);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ this_test_teardown();
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+#endif
+
+/**
+ * test_bool_array_out_in()
+ * test serialization and subsequent deserialization of a boolean array
+ */
+static void test_bool_array_out_in(void)
+{
+ const int NUMDIMS = 2;
+ boolean x[2][3] = { { TRUE, FALSE, TRUE, }, { FALSE, TRUE, FALSE, }, };
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_nativearray* natarray = NULL;
+ etch_arrayvalue* arrayval = NULL;
+ etch_arrayvalue* reconstituted_arrayval = NULL;
+ etch_validator* validator = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ natarray = new_etch_nativearray_from(x, CLASSID_ARRAY_BOOL, sizeof(boolean), NUMDIMS, 3, 2, 0);
+ arrayval = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE, NULL, 2, 0, FALSE);
+ validator = etchvtor_boolean_get(NUMDIMS);
+
+ this_test_setup();
+ ((struct i_value_factory*)((etch_object*)vf)->vtab)->add_type(vf, mt_footype); /* the type we are testing with */
+ objcount = arraycount = 0; /* initialize global counters */
+
+ /* test serialization and deserialization of the array object as a whole.
+ * we are returned an object which is the reconstituted array after it has
+ * been serialized and subsequently deserialized */
+ reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (etch_object*) arrayval, validator);
+
+ CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);
+
+ /* recursively test serialization and deserialization of all the components
+ * of the array object, including the array itself, subarrays, and scalars,
+ * as per the java version of this test. this really ensures that we have
+ * all our memory management ducks in a row. */
+
+ do_recursive_test (vf, (etch_object*) arrayval, validator);
+
+ etch_object_destroy(arrayval);
+ etch_object_destroy(reconstituted_arrayval);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ this_test_teardown();
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_short_array_out_in()
+ * test serialization and subsequent deserialization of a short integer array
+ */
+static void test_short_array_out_in(void)
+{
+ const int NUMDIMS = 2;
+ short x[2][3] = { { 0, 1, 2, }, { -1, -32768, 32767, }, };
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_nativearray* natarray = NULL;
+ etch_arrayvalue* arrayval = NULL;
+ etch_arrayvalue* reconstituted_arrayval = NULL;
+ etch_validator* validator = NULL;
+
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ natarray = new_etch_nativearray_from(x, CLASSID_ARRAY_INT16, sizeof(short), NUMDIMS, 3, 2, 0);
+ arrayval = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_SHORT, NULL, 2, 0, FALSE);
+ validator = etchvtor_int16_get(NUMDIMS);
+
+ this_test_setup();
+ ((struct i_value_factory*)((etch_object*)vf)->vtab)->add_type(vf, mt_footype); /* the type we are testing with */
+ objcount = arraycount = 0; /* initialize global counters */
+
+ /* test serialization and deserialization of the array object as a whole.
+ * we are returned an object which is the reconstituted array after it has
+ * been serialized and subsequently deserialized */
+ reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (etch_object*) arrayval, validator);
+
+ CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);
+
+ /* recursively test serialization and deserialization of all the components
+ * of the array object, including the array itself, subarrays, and scalars,
+ * as per the java version of this test. this really ensures that we have
+ * all our memory management ducks in a row. */
+
+ do_recursive_test (vf, (etch_object*) arrayval, validator);
+
+ etch_object_destroy(arrayval);
+ etch_object_destroy(reconstituted_arrayval);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ this_test_teardown();
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_int_array_out_in()
+ * test serialization and subsequent deserialization of an int array
+ */
+static void test_int_array_out_in(void)
+{
+ const int NUMDIMS = 2;
+ int x[2][3] = { { 0, 1, 2, }, { -1, ETCHTYPE_MIN_INT32, ETCHTYPE_MAX_INT32, }, };
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_nativearray* natarray = NULL;
+ etch_arrayvalue* arrayval = NULL;
+ etch_arrayvalue* reconstituted_arrayval = NULL;
+ etch_validator* validator = NULL;
+
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ natarray = new_etch_nativearray_from(x, CLASSID_ARRAY_INT32, sizeof(int), NUMDIMS, 3, 2, 0);
+ arrayval = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_INT, NULL, 2, 0, FALSE);
+ validator = etchvtor_int32_get(NUMDIMS);
+
+ this_test_setup();
+ ((struct i_value_factory*)((etch_object*)vf)->vtab)->add_type(vf, mt_footype); /* the type we are testing with */
+ objcount = arraycount = 0; /* initialize global counters */
+
+ /* test serialization and deserialization of the array object as a whole.
+ * we are returned an object which is the reconstituted array after it has
+ * been serialized and subsequently deserialized */
+ reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (etch_object*) arrayval, validator);
+
+ CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);
+
+ /* recursively test serialization and deserialization of all the components
+ * of the array object, including the array itself, subarrays, and scalars,
+ * as per the java version of this test. this really ensures that we have
+ * all our memory management ducks in a row. */
+
+ do_recursive_test (vf, (etch_object*) arrayval, validator);
+
+ etch_object_destroy(arrayval);
+ etch_object_destroy(reconstituted_arrayval);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ this_test_teardown();
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_long_array_out_in()
+ * test serialization and subsequent deserialization of a long array
+ */
+static void test_long_array_out_in(void)
+{
+ const int NUMDIMS = 2;
+ int64 x[2][3] = { { 0, 1, 2, }, { -1, ETCHTYPE_MIN_INT64, ETCHTYPE_MAX_INT64, }, };
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_nativearray* natarray = NULL;
+ etch_arrayvalue* arrayval = NULL;
+ etch_arrayvalue* reconstituted_arrayval = NULL;
+ etch_validator* validator = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ natarray = new_etch_nativearray_from(x, CLASSID_ARRAY_INT64, sizeof(int64), NUMDIMS, 3, 2, 0);
+ arrayval = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_LONG, NULL, 2, 0, FALSE);
+ validator = etchvtor_int64_get(NUMDIMS);
+
+ this_test_setup();
+ ((struct i_value_factory*)((etch_object*)vf)->vtab)->add_type(vf, mt_footype); /* the type we are testing with */
+ objcount = arraycount = 0; /* initialize global counters */
+
+ /* test serialization and deserialization of the array object as a whole.
+ * we are returned an object which is the reconstituted array after it has
+ * been serialized and subsequently deserialized */
+ reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (etch_object*) arrayval, validator);
+
+ CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);
+
+ /* recursively test serialization and deserialization of all the components
+ * of the array object, including the array itself, subarrays, and scalars,
+ * as per the java version of this test. this really ensures that we have
+ * all our memory management ducks in a row. */
+
+ do_recursive_test (vf, (etch_object*) arrayval, validator);
+
+ etch_object_destroy(arrayval);
+ etch_object_destroy(reconstituted_arrayval);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ this_test_teardown();
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_float_array_out_in()
+ * test serialization and subsequent deserialization of a float array
+ */
+static void test_float_array_out_in(void)
+{
+ const int NUMDIMS = 2;
+ float x[2][3] = { { 0.0, 1.0, 2.0, }, { -1.0, ETCHTYPE_MIN_FLOAT, ETCHTYPE_MAX_FLOAT, }, };
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_nativearray* natarray = NULL;
+ etch_arrayvalue* arrayval = NULL;
+ etch_arrayvalue* reconstituted_arrayval = NULL;
+ etch_validator* validator = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ natarray = new_etch_nativearray_from(x, CLASSID_ARRAY_FLOAT, sizeof(float), NUMDIMS, 3, 2, 0);
+ arrayval = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_FLOAT, NULL, 2, 0, FALSE);
+ validator = etchvtor_float_get(NUMDIMS);
+
+ this_test_setup();
+ ((struct i_value_factory*)((etch_object*)vf)->vtab)->add_type(vf, mt_footype); /* the type we are testing with */
+ objcount = arraycount = 0; /* initialize global counters */
+
+ /* test serialization and deserialization of the array object as a whole.
+ * we are returned an object which is the reconstituted array after it has
+ * been serialized and subsequently deserialized */
+ reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (etch_object*) arrayval, validator);
+
+ CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);
+
+ /* recursively test serialization and deserialization of all the components
+ * of the array object, including the array itself, subarrays, and scalars,
+ * as per the java version of this test. this really ensures that we have
+ * all our memory management ducks in a row. */
+
+ do_recursive_test (vf, (etch_object*) arrayval, validator);
+
+ etch_object_destroy(arrayval);
+ etch_object_destroy(reconstituted_arrayval);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ this_test_teardown();
+
+ // TODO: cleanup statics
+ // etchvf_free_builtins()
+
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_double_array_out_in()
+ * test serialization and subsequent deserialization of a double array
+ */
+static void test_double_array_out_in(void)
+{
+ const int NUMDIMS = 2;
+ double x[2][3] = { { 0.0, 1.0, 2.0, }, { -1.0, ETCHTYPE_MIN_DOUBLE, ETCHTYPE_MAX_DOUBLE, }, };
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_nativearray* natarray = NULL;
+ etch_arrayvalue* arrayval = NULL;
+ etch_arrayvalue* reconstituted_arrayval = NULL;
+ etch_validator* validator = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ natarray = new_etch_nativearray_from(x, CLASSID_ARRAY_DOUBLE, sizeof(double), NUMDIMS, 3, 2, 0);
+ arrayval = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_DOUBLE, NULL, 2, 0, FALSE);
+ validator = etchvtor_double_get(NUMDIMS);
+
+ this_test_setup();
+ ((struct i_value_factory*)((etch_object*)vf)->vtab)->add_type(vf, mt_footype); /* the type we are testing with */
+ objcount = arraycount = 0; /* initialize global counters */
+
+ /* test serialization and deserialization of the array object as a whole.
+ * we are returned an object which is the reconstituted array after it has
+ * been serialized and subsequently deserialized */
+ reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (etch_object*) arrayval, validator);
+
+ CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);
+
+ /* recursively test serialization and deserialization of all the components
+ * of the array object, including the array itself, subarrays, and scalars,
+ * as per the java version of this test. this really ensures that we have
+ * all our memory management ducks in a row. */
+
+ do_recursive_test (vf, (etch_object*) arrayval, validator);
+
+ etch_object_destroy(arrayval);
+ etch_object_destroy(reconstituted_arrayval);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ this_test_teardown();
+
+ // TODO: cleanup statics
+ // etchvf_free_builtins()
+
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+static etch_string* ns(int n)
+{
+ wchar_t buf[32]; etch_snwprintf(buf, 32, L"%d", (size_t) n);
+ return new_stringw(buf);
+}
+
+
+/**
+ * test_string_array_out_in()
+ * test serialization and subsequent deserialization of a string array
+ */
+static void test_string_array_out_in(void)
+{
+ const int NUMDIMS = 2;
+ etch_string* x[2][3] = { { ns(0), ns(1), ns(2), }, { ns(3), ns(4), ns(5), }, };
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_nativearray* natarray = NULL;
+ etch_arrayvalue* arrayval = NULL;
+ etch_arrayvalue* reconstituted_arrayval = NULL;
+ etch_validator* validator = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+
+ natarray = new_etch_nativearray_from(x, CLASSID_ARRAY_STRING, sizeof(void*), NUMDIMS, 3, 2, 0);
+ arrayval = new_arrayvalue_from(natarray, ETCH_XTRNL_TYPECODE_STRING, NULL, 2, 0, FALSE);
+ validator = etchvtor_string_get(NUMDIMS);
+
+ this_test_setup();
+ ((struct i_value_factory*)((etch_object*)vf)->vtab)->add_type(vf, mt_footype); /* the type we are testing with */
+ objcount = arraycount = 0; /* initialize global counters */
+
+ /* test serialization and deserialization of the array object as a whole.
+ * we are returned an object which is the reconstituted array after it has
+ * been serialized and subsequently deserialized */
+ reconstituted_arrayval = (etch_arrayvalue*) do_tdo_tdi_test(vf, (etch_object*) arrayval, validator);
+
+ CU_ASSERT_EQUAL_FATAL(is_etch_arrayvalue(reconstituted_arrayval), TRUE);
+
+ /* recursively test serialization and deserialization of all the components
+ * of the array object, including the array itself, subarrays, and scalars,
+ * as per the java version of this test. this really ensures that we have
+ * all our memory management ducks in a row. */
+
+ do_recursive_test (vf, (etch_object*) arrayval, validator);
+
+ etch_object_destroy(arrayval);
+ etch_object_destroy(reconstituted_arrayval);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ this_test_teardown();
+
+ // TODO: cleanup statics
+ // etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+#ifdef _WIN32
+#pragma message ( "test_add_perf deactivated" )
+#else
+#warning "test_add_perf deactivated"
+#endif
+
+#if 0
+/**
+ * test_add_perf()
+ * test performance of add(x,y) message serialization and delivery
+ */
+static void test_add_perf(void)
+{
+ int result = 0;
+ etch_who* whofrom = NULL;
+ etch_message* msg = NULL;
+ etch_messagizer* messagizer = NULL;
+ i_transportpacket* my_packetsource = NULL;
+ my_impl_transportpacket* my_impl_packetsource = NULL;
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_type* addtype = NULL;
+ etch_field* xfield = NULL;
+ etch_field* yfield = NULL;
+ etch_resources* resx = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ resx = new_etch_resources(ETCH_RESOURCES_DEFSIZE);
+
+ addtype = new_type(L"add");
+ xfield = new_field(L"x");
+ yfield = new_field(L"y");
+
+ etchtype_put_validator(addtype, clone_field(xfield), (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(addtype, clone_field(yfield), (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(addtype, clone_field(builtins._mf__message_id), (etch_object*) etchvtor_int64_get(0));
+
+ msg = new_message(addtype, ETCH_DEFSIZE, (etch_value_factory*) vf);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msg);
+ message_put(msg, clone_field(xfield), (etch_object*) new_int32(1)); /* relinquish all content */
+ message_put(msg, clone_field(yfield), (etch_object*) new_int32(2));
+ message_put(msg, clone_field(builtins._mf__message_id), (etch_object*) new_int64(0x0123456789abcdefLL));
+
+ /* we relinquish vf to the resources destructor here */
+ etch_resources_add(resx, ETCH_RESXKEY_MSGIZER_VALUFACT, (etch_object*) vf);
+
+ /* instantiate object which implements i_transportpacket and extract its interface.
+ * the interface's destructor is coded to destroy its instantiating object. */
+ my_impl_packetsource = new_my_impl_transportpacket();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_impl_packetsource);
+ my_packetsource = my_impl_packetsource->ixp; /* extract interface */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_packetsource);
+
+ messagizer = new_messagizer(my_packetsource, L"foo:?Messagizer.format=binary", resx);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(messagizer);
+
+ /* we need to continue to use the message in this test, so since the message's
+ * destructor will be called by messagizer.transport_message(), mark the message
+ * static such that it is protected from destruction.
+ */
+ set_etchobj_static_all(msg);
+
+ /* we ordinarily relinquish msg on success, retain it on failure;
+ * however here we have retained it regardless, since we marked it
+ * static above, thus disabling its destructor.
+ */
+ result = messagizer->transport_message(messagizer, whofrom, msg);
+ CU_ASSERT_EQUAL(result, 0);
+
+ if (0 == result)
+ { int i = 0;
+ const int n = 900973; /* this code is ported from the java test */
+
+ for(i = 0; i < 3; i++)
+ do_perftest("test_add_perf", i, messagizer, msg, n);
+ }
+
+ clear_etchobj_static_all(msg); /* clear static state so we can destroy it now */
+ etch_object_destroy(msg);
+
+ etch_object_destroy(xfield);
+ etch_object_destroy(yfield);
+ etch_object_destroy(addtype);
+
+ etch_object_destroy(resx); /* content is destroyed also (vf in this case) */
+ etch_object_destroy(messagizer);
+ etch_object_destroy(my_packetsource); /* destroys my_impl_transportpacket also */
+
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+#endif
+
+/**
+ * get_bytearray()
+ * support for test_value_to_bytes().
+ * return an etch_nativearray wrapping the supplied static byte* array.
+ */
+static etch_nativearray* get_bytearray(byte* bytes, const int bytecount)
+{
+ etch_nativearray* na = new_etch_nativearray_from(bytes, CLASSID_ARRAY_BYTE, sizeof(byte), 1, bytecount, 0, 0);
+ na->content_obj_type = ETCHTYPEB_BYTES;
+ na->content_class_id = CLASSID_NONE; /* unwrapped */
+ return na;
+}
+
+
+ /**
+ * assert_value_to_bytes()
+ * support for test_value_to_bytes() -- serialize value object, and assert
+ * that the serialized bytes match the supplied expected byte array.
+ */
+static int assert_value_to_bytes(etch_object* value, etch_nativearray* expected_bytes, etch_value_factory* vf)
+{
+ byte* buf = NULL;
+ int bytecount = 0, i = 0, result = 0, errs = 0;
+ etch_flexbuffer* fbuf = new_flexbuffer(ETCH_DEFSIZE);
+ etch_validator* vtor = etchvtor_object_get(0);
+
+ binary_tagged_data_output* tdo = new_binary_tagdata_output(vf, fbuf);
+
+ /* serialize the specified value object to the buffer fbuf */
+ result = bintdo_write_value(tdo, vtor, value);
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result) errs++;
+
+ if (0 == result)
+ {
+ fbuf->index = 0; /* get the serialized bytes to a new buffer */
+ buf = etch_flexbuf_get_all(fbuf, (size_t*) &bytecount);
+
+ printf("%d <-> %d\n", bytecount, (int)expected_bytes->bytecount);
+ CU_ASSERT_EQUAL(bytecount, expected_bytes->bytecount);
+ if (bytecount != expected_bytes->bytecount) errs++;
+
+ if (bytecount == expected_bytes->bytecount)
+ { /* verify that serialized bytes match expected bytes */
+ for(i = 0; i < bytecount; i++)
+ { signed char a = buf[i], b = 0;
+ expected_bytes->get1(expected_bytes, &b, i);
+ result = ((int) a == (int) b);
+ CU_ASSERT_EQUAL(result, TRUE);
+ if (!result)
+ errs++;
+ }
+ }
+ }
+
+ /* free memory */
+ etch_object_destroy(value);
+ etch_object_destroy(expected_bytes);
+ etch_object_destroy(tdo);
+ etch_object_destroy(fbuf);
+ etch_free(buf);
+
+ return errs? -1: 0;
+}
+
+
+/**
+ * test_single_value_to_bytes()
+ * test that various value objects serialized by the tdo match hard-coded expected byte arrays
+ */
+static void test_single_value_to_bytes(void)
+{
+ int result = 0;
+ etch_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = (etch_value_factory*)new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ do /* NULL */
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_NULL };
+ result = assert_value_to_bytes(NULL, get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do /* boolean primitive objects */
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE };
+ result = assert_value_to_bytes((etch_object*) new_boolean(FALSE), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE };
+ result = assert_value_to_bytes((etch_object*) new_boolean(TRUE), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do /* some tiny integers */
+ { signed char a[] = { 0 };
+ result = assert_value_to_bytes((etch_object*) new_byte(0), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do
+ { signed char a[] = { 1 };
+ result = assert_value_to_bytes((etch_object*) new_byte(1), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do
+ { signed char a[] = { -1 };
+ result = assert_value_to_bytes((etch_object*) new_byte(-1), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do /* non-tinyint byte */
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_BYTE, -100 };
+ result = assert_value_to_bytes((etch_object*) new_byte(-100), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do /* some short integers */
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_SHORT, 39, 16 };
+ result = assert_value_to_bytes((etch_object*) new_int16(10000), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_SHORT, -40, -16 };
+ result = assert_value_to_bytes((etch_object*) new_int16(-10000), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do /* 32-bit integers */
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_INT, 59, -102, -54, 0 };
+ result = assert_value_to_bytes((etch_object*) new_int32(1000000000), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_INT, -60, 101, 54, 0 };
+ result = assert_value_to_bytes((etch_object*) new_int32(-1000000000), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do /* long integers */
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_LONG, 13, -32, -74, -77, -89, 100, 0, 0 };
+ result = assert_value_to_bytes((etch_object*) new_int64(1000000000000000000LL), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_LONG, -14, 31, 73, 76, 88, -100, 0, 0 };
+ result = assert_value_to_bytes((etch_object*) new_int64(-1000000000000000000LL), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do /* float */
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_FLOAT, 70, 64, -28, 0 };
+ result = assert_value_to_bytes((etch_object*) new_float(12345.0), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ do /* double */
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_DOUBLE, 64, -42, -24, 0, 0, 0, 0, 0 };
+ result = assert_value_to_bytes((etch_object*) new_double(23456.0), get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+ } while(0);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_boolean_array_value_to_bytes()
+ * test that various value objects serialized by the tdo match hard-coded expected byte arrays
+ */
+static void test_boolean_array_value_to_bytes(void)
+{
+ int result = 0;
+ etch_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = (etch_value_factory*)new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ do /* boolean[] */
+ { const int NUMDIMS1 = 1, ITEMCOUNT = 2;
+
+ signed char a[] =
+ { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
+ ETCH_XTRNL_TYPECODE_BOOLEAN_CONTENT, /* array content type */
+ NUMDIMS1, /* dimensions count */
+ ITEMCOUNT, /* count of content items following */
+ ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE, /* item[0] */
+ ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE, /* item[1] */
+ ETCH_XTRNL_TYPECODE_NONE /* eod mark */
+ };
+
+ etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_BOOLEAN_CONTENT,
+ NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);
+
+ arrayvalue_add(av, (etch_object*) new_boolean(TRUE));
+ arrayvalue_add(av, (etch_object*) new_boolean(FALSE));
+
+ result = assert_value_to_bytes((etch_object*) av, get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_byte_array_value_to_bytes()
+ * test that various value objects serialized by the tdo match hard-coded expected byte arrays
+ */
+static void test_byte_array_value_to_bytes(void)
+{
+ int result = 0;
+ etch_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = (etch_value_factory*)new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ do /* byte[] */
+ { const int ITEMCOUNT = 3;
+ signed char content[] = { 1, 2, 3 };
+
+ signed char a[] = /* byte vector is a special case, not an array type */
+ { ETCH_XTRNL_TYPECODE_BYTES, /* array content type */
+ ITEMCOUNT, /* count of items following */
+ 1, /* item[0] */
+ 2, /* item[1] */
+ 3 /* item[2] */
+ };
+
+ etch_nativearray* expected = get_bytearray((byte*)a, sizeof(a));
+
+ /* memory management notes: here we create a nativearray na from static
+ * content. new_etch_nativearray_from() defaults nativearray.is_content_owned
+ * to false, so when the nativearray na is destroyed, it will not attempt
+ * to free memory for the static content bytes. we then create an arrayvalue
+ * from nativearray na, specifying FALSE for parameter 6, is_readonly. the
+ * arrayvalue destructor will as a result destroy the content nativearray,
+ * which will in turn *not* destroy its content bytearray. finally, we
+ * relinquish the arrayvalue to assert_value_to_bytes(), which calls the
+ * arrayvalue destructor, causing the above to occur.
+ */
+ etch_nativearray* na = new_etch_nativearray_from(content, CLASSID_ARRAY_BYTE, sizeof(byte), 1, ITEMCOUNT, 0, 0);
+ etch_arrayvalue* av = new_arrayvalue_from(na, ETCH_XTRNL_TYPECODE_BYTE, NULL, ITEMCOUNT, 0, FALSE);
+
+ result = assert_value_to_bytes((etch_object*) av, expected, vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ do /* byte[][] */
+ {
+ #define NUMDIMS2 2
+ #define ITEMCOUNT2 2
+ #define ITEMCOUNT3 3
+ signed char content[NUMDIMS2][ITEMCOUNT3] = { { 1, 2, 3, }, { 4, 5, 6, }, };
+
+ signed char a[] = /* array[2] of byte vectors */
+ { ETCH_XTRNL_TYPECODE_ARRAY, /* value type array */
+ ETCH_XTRNL_TYPECODE_BYTE, /* array content type */
+ NUMDIMS2, /* dimensions count */
+ ITEMCOUNT2, /* item count in dim[1] */
+ ETCH_XTRNL_TYPECODE_BYTES, /* byte vector follows */
+ ITEMCOUNT3, /* count of items following */
+ 1, /* item[0][0] */
+ 2, /* item[0][1] */
+ 3, /* item[0][2] */
+ ETCH_XTRNL_TYPECODE_BYTES, /* byte vector follows */
+ ITEMCOUNT3, /* count of items following */
+ 4, /* item[1][0] */
+ 5, /* item[1][1] */
+ 6, /* item[1][2] */
+ ETCH_XTRNL_TYPECODE_NONE /* eod mark */
+ };
+
+ etch_nativearray* expected = get_bytearray((byte*)a, sizeof(a));
+
+ /* memory management notes: here we create a nativearray na from static
+ * content. new_etch_nativearray_from() defaults nativearray.is_content_owned
+ * to false, so when the nativearray na is destroyed, it will not attempt
+ * to free memory for the static content bytes. we then create an arrayvalue
+ * from nativearray na, specifying FALSE for parameter 6, is_readonly. the
+ * arrayvalue destructor will as a result destroy the content nativearray,
+ * which will in turn *not* destroy its content bytearray. finally, we
+ * relinquish the arrayvalue to assert_value_to_bytes(), which calls the
+ * arrayvalue destructor, causing the above to occur.
+ */
+ etch_nativearray* na = new_etch_nativearray_from(content, CLASSID_ARRAY_BYTE,
+ sizeof(byte), NUMDIMS2, ITEMCOUNT3, ITEMCOUNT2, 0);
+
+ /* note here that the arrayvalue content type (ETCH_XTRNL_TYPECODE_BYTE)
+ * is the content type serialized, so it must be the same as a[1].
+ * the serialized type of arrays of byte vectors is BYTE, as defined by the
+ * tagged data spec, and not BYTES, which one might expect
+ */
+ etch_arrayvalue* av = new_arrayvalue_from(na, ETCH_XTRNL_TYPECODE_BYTE,
+ NULL, ETCH_DEFSIZE, 0, FALSE);
+
+ result = assert_value_to_bytes((etch_object*) av, expected, vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_short_array_value_to_bytes()
+ * test that various value objects serialized by the tdo match hard-coded expected byte arrays
+ */
+static void test_short_array_value_to_bytes(void)
+{
+ int result = 0;
+ etch_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = (etch_value_factory*)new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ do /* short[] */
+ { const int NUMDIMS1 = 1, ITEMCOUNT = 3;
+
+ signed char a[] =
+ { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
+ ETCH_XTRNL_TYPECODE_SHORT, /* array content type */
+ NUMDIMS1, /* dimensions count */
+ ITEMCOUNT, /* count of content items following */
+ 1, /* item[0] */
+ 2, /* item[1] */
+ 3, /* item[2] */
+ ETCH_XTRNL_TYPECODE_NONE /* eod mark */
+ };
+
+ etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_SHORT,
+ NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);
+
+ arrayvalue_add(av, (etch_object*) new_int16(1));
+ arrayvalue_add(av, (etch_object*) new_int16(2));
+ arrayvalue_add(av, (etch_object*) new_int16(3));
+
+ result = assert_value_to_bytes((etch_object*) av, get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_int_array_value_to_bytes()
+ * test that various value objects serialized by the tdo match hard-coded expected byte arrays
+ */
+static void test_int_array_value_to_bytes(void)
+{
+ int result = 0;
+ etch_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = (etch_value_factory*)new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+
+ do /* short[] */
+ { const int NUMDIMS1 = 1, ITEMCOUNT = 3;
+
+ signed char a[] =
+ { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
+ ETCH_XTRNL_TYPECODE_INT, /* array content type */
+ NUMDIMS1, /* dimensions count */
+ ITEMCOUNT, /* count of content items following */
+ 1, /* item[0] */
+ 2, /* item[1] */
+ 3, /* item[2] */
+ ETCH_XTRNL_TYPECODE_NONE /* eod mark */
+ };
+
+ etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_INT,
+ NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);
+
+ arrayvalue_add(av, (etch_object*) new_int32(1));
+ arrayvalue_add(av, (etch_object*) new_int32(2));
+ arrayvalue_add(av, (etch_object*) new_int32(3));
+
+ result = assert_value_to_bytes((etch_object*) av, get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_long_array_value_to_bytes()
+ * test that various value objects serialized by the tdo match hard-coded expected byte arrays
+ */
+static void test_long_array_value_to_bytes(void)
+{
+ int result = 0;
+ etch_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = (etch_value_factory*)new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ do /* short[] */
+ { const int NUMDIMS1 = 1, ITEMCOUNT = 3;
+
+ signed char a[] =
+ { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
+ ETCH_XTRNL_TYPECODE_LONG, /* array content type */
+ NUMDIMS1, /* dimensions count */
+ ITEMCOUNT, /* count of content items following */
+ 1, /* item[0] */
+ 2, /* item[1] */
+ 3, /* item[2] */
+ ETCH_XTRNL_TYPECODE_NONE /* eod mark */
+ };
+
+ etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_LONG,
+ NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);
+
+ arrayvalue_add(av, (etch_object*) new_int64(1));
+ arrayvalue_add(av, (etch_object*) new_int64(2));
+ arrayvalue_add(av, (etch_object*) new_int64(3));
+
+ result = assert_value_to_bytes((etch_object*) av, get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_float_array_value_to_bytes()
+ * test that various value objects serialized by the tdo match hard-coded expected byte arrays
+ */
+static void test_float_array_value_to_bytes(void)
+{
+ int result = 0;
+ etch_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = (etch_value_factory*)new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+
+ do /* short[] */
+ { const int NUMDIMS1 = 1, ITEMCOUNT = 3;
+
+ signed char a[] =
+ { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
+ ETCH_XTRNL_TYPECODE_FLOAT, /* array content type */
+ NUMDIMS1, /* dimensions count */
+ ITEMCOUNT, /* count of content items following */
+ ETCH_XTRNL_TYPECODE_FLOAT, 63,-128, 0, 0, /* item[0] */
+ ETCH_XTRNL_TYPECODE_FLOAT, 64, 0, 0, 0, /* item[1] */
+ ETCH_XTRNL_TYPECODE_FLOAT, 64, 64, 0, 0, /* item[2] */
+ ETCH_XTRNL_TYPECODE_NONE /* eod mark */
+ };
+
+ etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_FLOAT,
+ NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);
+
+ arrayvalue_add(av, (etch_object*) new_float(1.0));
+ arrayvalue_add(av, (etch_object*) new_float(2.0));
+ arrayvalue_add(av, (etch_object*) new_float(3.0));
+
+ result = assert_value_to_bytes((etch_object*) av, get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_double_array_value_to_bytes()
+ * test that various value objects serialized by the tdo match hard-coded expected byte arrays
+ */
+static void test_double_array_value_to_bytes(void)
+{
+ int result = 0;
+ etch_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = (etch_value_factory*)new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ do /* short[] */
+ { const int NUMDIMS1 = 1, ITEMCOUNT = 3;
+
+ signed char a[] =
+ { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
+ ETCH_XTRNL_TYPECODE_DOUBLE, /* array content type */
+ NUMDIMS1, /* dimensions count */
+ ITEMCOUNT, /* count of content items following */
+ ETCH_XTRNL_TYPECODE_DOUBLE, 63,-16, 0, 0, 0, 0, 0, 0, /* item[0] */
+ ETCH_XTRNL_TYPECODE_DOUBLE, 64, 0, 0, 0, 0, 0, 0, 0, /* item[1] */
+ ETCH_XTRNL_TYPECODE_DOUBLE, 64, 8, 0, 0, 0, 0, 0, 0, /* item[2] */
+ ETCH_XTRNL_TYPECODE_NONE /* eod mark */
+ };
+
+ etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_DOUBLE,
+ NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);
+
+ arrayvalue_add(av, (etch_object*) new_double(1.0));
+ arrayvalue_add(av, (etch_object*) new_double(2.0));
+ arrayvalue_add(av, (etch_object*) new_double(3.0));
+
+ result = assert_value_to_bytes((etch_object*)av, get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_string_values_to_bytes()
+ * test that various value objects serialized by the tdo match hard-coded expected byte arrays
+ */
+static void test_string_values_to_bytes(void)
+{
+ int result = 0;
+ etch_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = (etch_value_factory*)new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ printf("1\n");
+
+ do
+ { signed char a[] = { ETCH_XTRNL_TYPECODE_EMPTY_STRING };
+
+ result = assert_value_to_bytes((etch_object*) new_stringw(L""),
+ get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ printf("2\n");
+
+ do /* string */
+ { /* note here that our test object is a unicode string, while our expected byte
+ * array encodes an array of 8-bit characters. this is because we carry all
+ * strings internally as unicode, but the tdo converts strings to the configured
+ * wire encoding format, which is utf-8 at this writing.
+ */
+ const int STRLEN = 3;
+ signed char a[] = { ETCH_XTRNL_TYPECODE_STRING, STRLEN, 97, 98, 99 };
+
+ result = assert_value_to_bytes((etch_object*) new_stringw(L"abc"),
+ get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+ printf("3\n");
+
+ do /* string[] */
+ { const int NUMDIMS1 = 1, ITEMCOUNT = 3, STRLEN1 = 1;
+
+ signed char a[] =
+ { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
+ ETCH_XTRNL_TYPECODE_STRING, /* array content type */
+ NUMDIMS1, /* dimensions count */
+ ITEMCOUNT, /* count of content items following */
+ ETCH_XTRNL_TYPECODE_STRING, STRLEN1, 97, /* item[0] */
+ ETCH_XTRNL_TYPECODE_EMPTY_STRING, /* item[1] */
+ ETCH_XTRNL_TYPECODE_STRING, STRLEN1, 99, /* item[2] */
+ ETCH_XTRNL_TYPECODE_NONE /* eod mark */
+ };
+
+ etch_arrayvalue* av = new_arrayvalue(ETCH_XTRNL_TYPECODE_STRING,
+ NULL, NUMDIMS1, ITEMCOUNT, 0, FALSE, FALSE);
+
+ arrayvalue_add(av, (etch_object*) new_stringw(L"a"));
+ arrayvalue_add(av, (etch_object*) new_stringw(L""));
+ arrayvalue_add(av, (etch_object*) new_stringw(L"c"));
+
+ result = assert_value_to_bytes((etch_object*)av, get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+ printf("4\n");
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_date_values_to_bytes()
+ * test that various value objects serialized by the tdo match hard-coded expected byte arrays
+ */
+static void test_date_values_to_bytes(void)
+{
+ int result = 0;
+ etch_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = (etch_value_factory*)new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ do /* date */
+ { const int64 BOGUSDATEVAL = 1234567890;
+
+ signed char a[] =
+ { ETCH_XTRNL_TYPECODE_CUSTOM, /* indicates struct follows */
+ ETCH_XTRNL_TYPECODE_INT, 43, 57, 107, -52, /* _mt__etch_datetime type id */
+ 1, /* struct member count */
+ ETCH_XTRNL_TYPECODE_INT, 102, 0, 26, 64, /* "dateTime" field id */
+ ETCH_XTRNL_TYPECODE_INT, 73, -106, 2, -46, /* serialized 1234567890 */
+ ETCH_XTRNL_TYPECODE_NONE /* eod mark */
+ };
+
+ etch_date* testdate = new_date();
+ testdate->value = (time_t) BOGUSDATEVAL;
+
+ result = assert_value_to_bytes((etch_object*)testdate, get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ do /* date[] */
+ { const int NUMDIMS1 = 1, ITEMCOUNT1 = 1;
+ const int64 BOGUSDATEVAL1 = 1234567890LL;
+ const int64 BOGUSDATEVAL2 = 2345678901LL;
+
+ signed char a[] =
+ { ETCH_XTRNL_TYPECODE_ARRAY, /* array type */
+ ETCH_XTRNL_TYPECODE_CUSTOM, /* array content type */
+ ETCH_XTRNL_TYPECODE_INT, 43, 57, 107, -52, /* "custom struct type" _mt__etch_datetime */
+ NUMDIMS1, /* array dimensions */
+ ITEMCOUNT2, /* count of array content items following */
+
+ ETCH_XTRNL_TYPECODE_CUSTOM, /* start array member[0] */
+ ETCH_XTRNL_TYPECODE_INT, 43, 57, 107, -52, /* struct type _mt__etch_datetime */
+ ITEMCOUNT1, /* struct item count */
+ ETCH_XTRNL_TYPECODE_INT, 102, 0, 26, 64, /* "dateTime" field id */
+ ETCH_XTRNL_TYPECODE_INT, 73, -106, 2, -46, /* serialized 1234567890 */
+ ETCH_XTRNL_TYPECODE_NONE, /* eod mark for struct - end of array member[0] */
+
+ ETCH_XTRNL_TYPECODE_CUSTOM, /* start array member[1] */
+ ETCH_XTRNL_TYPECODE_INT, 43, 57, 107, -52, /* struct type _mt__etch_datetime */
+ ITEMCOUNT1, /* struct item count */
+ ETCH_XTRNL_TYPECODE_INT, 102, 0, 26, 64, /* "dateTime" field id */
+ ETCH_XTRNL_TYPECODE_LONG, 0, 0, 0, 0, -117, -48, 56, 53, /* serialized 2345678901 */
+ ETCH_XTRNL_TYPECODE_NONE, /* eod mark for struct - end of array member[1] */
+
+ ETCH_XTRNL_TYPECODE_NONE, /* eod mark for array */
+ };
+
+ etch_arrayvalue* av = NULL;
+ etch_type* my_custom_struct_type = builtins._mt__etch_datetime;
+
+ etch_date* date1 = new_date();
+ etch_date* date2 = new_date();
+ date1->value = BOGUSDATEVAL1;
+ date2->value = BOGUSDATEVAL2;
+
+ av = new_arrayvalue(ETCH_XTRNL_TYPECODE_CUSTOM,
+ my_custom_struct_type, NUMDIMS1, ITEMCOUNT2, 0, FALSE, FALSE);
+
+ arrayvalue_add(av, (etch_object*) date1);
+ arrayvalue_add(av, (etch_object*) date2);
+
+ result = assert_value_to_bytes((etch_object*) av, get_bytearray((byte*)a, sizeof(a)), vf);
+ CU_ASSERT_EQUAL(result,0);
+
+ } while(0);
+
+ // destroy vf
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_binarytdit_suite()
+{
+ CU_pSuite ps = CU_add_suite("bintditdo test suite", init_suite, clean_suite);
+
+ CU_add_test(ps, "test constructor 0", test_constructor_0);
+ CU_add_test(ps, "test constructor 1", test_constructor_1);
+ CU_add_test(ps, "test constructor 2", test_constructor_2);
+ CU_add_test(ps, "test check value", test_check_value);
+
+ //TODO implement this test's functionality again!!!
+ //CU_add_test(ps, "test byte[][] out and back in", test_byte_array_out_in);
+ CU_add_test(ps, "test bool[][] out and back in", test_bool_array_out_in);
+ CU_add_test(ps, "test short[][] out and back in", test_short_array_out_in);
+
+ CU_add_test(ps, "test int[][] out and back in", test_int_array_out_in);
+ CU_add_test(ps, "test long[][] out and back in", test_long_array_out_in);
+ CU_add_test(ps, "test float[][] out and back in", test_float_array_out_in);
+ CU_add_test(ps, "test double[][] out and back in", test_double_array_out_in);
+ CU_add_test(ps, "test string[][] out and back in", test_string_array_out_in);
+// CU_add_test(ps, "test add perf", test_add_perf);
+
+ CU_add_test(ps, "test single values to bytes", test_single_value_to_bytes);
+ CU_add_test(ps, "test bool array to bytes", test_boolean_array_value_to_bytes);
+ CU_add_test(ps, "test byte arrays to bytes", test_byte_array_value_to_bytes);
+ CU_add_test(ps, "test short array to bytes", test_short_array_value_to_bytes);
+ CU_add_test(ps, "test int array to bytes", test_int_array_value_to_bytes);
+ CU_add_test(ps, "test long array to bytes", test_long_array_value_to_bytes);
+ CU_add_test(ps, "test float array to bytes", test_float_array_value_to_bytes);
+ CU_add_test(ps, "test double array to bytes", test_double_array_value_to_bytes);
+ CU_add_test(ps, "test string values to bytes", test_string_values_to_bytes);
+ CU_add_test(ps, "test date values to bytes", test_date_values_to_bytes);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/message/test_defvalufact.c b/binding-c/runtime/c/src/test/message/test_defvalufact.c
new file mode 100644
index 0000000..f5ea464
--- /dev/null
+++ b/binding-c/runtime/c/src/test/message/test_defvalufact.c
@@ -0,0 +1,2041 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_defvalufact.c
+ * test default impl of value factory
+ */
+#include "etch_runtime.h"
+#include "etch_validator.h" /* must be included second */
+#include "etch_tagged_data.h"
+#include "etch_map.h"
+#include "etch_log.h"
+#include "etch_id_name.h"
+#include "etch_default_value_factory.h"
+#include "etch_arrayval.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_nativearray.h"
+#include "etch_serializer.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+#include <wchar.h>
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * do_test_type_field
+ */
+static int do_test_type_field(default_value_factory* vf, etch_type* type, etch_field* key, etch_object* value)
+{
+ etch_message* msg = new_message(type, 1, (etch_value_factory*)vf);
+
+ const int result = message_put(msg, key, value);
+
+ etch_object_destroy(msg);
+
+ return result;
+}
+
+
+/* - - - - - - - - - - - - - -
+ * unit tests
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_constructor
+ * tests that all objects instatiated with a value factory such as builtin types
+ * and associated validators, are freed as expected.
+ */
+static void test_constructor(void)
+{
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* vf = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(vf);
+
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_builtin_types()
+ * test that the vf contains all expected default types
+ */
+static void test_builtin_types(void)
+{
+ int errs = 0;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* vf = NULL;
+ struct i_value_factory* vtab = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+ vtab = (struct i_value_factory*)((etch_object*)vf)->vtab;
+ if (NULL == vtab->get_type_by_name(vf, builtins._mt__etch_auth_exception->name))
+ errs++;
+ if (NULL == vtab->get_type_by_name(vf, builtins._mt__etch_datetime->name))
+ errs++;
+ if (NULL == vtab->get_type_by_name(vf, builtins._mt__etch_list->name))
+ errs++;
+ if (NULL == vtab->get_type_by_name(vf, builtins._mt__etch_map->name))
+ errs++;
+ if (NULL == vtab->get_type_by_name(vf, builtins._mt__etch_runtime_exception->name))
+ errs++;
+ if (NULL == vtab->get_type_by_name(vf, builtins._mt__etch_set->name))
+ errs++;
+ if (NULL == vtab->get_type_by_name(vf, builtins._mt__exception->name))
+ errs++;
+
+ CU_ASSERT_EQUAL(errs, 0);
+
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_add_type_a()
+ * test that a type can be added and its memory automatically freed.
+ */
+static void test_add_type_a(void)
+{
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* dvf = NULL;
+ etch_type* newtype = NULL;
+ etch_type* efftype = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ newtype = new_static_type(L"testtype");
+ efftype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->add_type(dvf, newtype); /* not owned by vf */
+ CU_ASSERT_PTR_EQUAL(newtype, efftype);
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ destroy_static_type(efftype); /* since not owned by vf */
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_add_type_b()
+ * test that a duplicate type is rejected as expected.
+ */
+static void test_add_type_b(void)
+{
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* dvf = NULL;
+ wchar_t* samestring = L"sametype";
+ etch_type* newtype1 = NULL;
+ etch_type* newtype2 = NULL;
+ etch_type* efftype = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ newtype1 = new_static_type(samestring);
+ newtype2 = new_static_type(samestring);
+
+ efftype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->add_type(dvf, newtype1); /* not owned by vf */
+ CU_ASSERT_PTR_EQUAL(efftype, newtype1);
+
+ /* when try to add a duplicate, vf returns original and destroys duplicate */
+ efftype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->add_type(dvf, newtype2);
+ CU_ASSERT_PTR_EQUAL(efftype, newtype1);
+
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ /* detroy newtype1 since not owned by vf */
+ destroy_static_type(newtype1);
+ /* don't destroy newtype2 since vf destroyed it */
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_add_type_c()
+ * test that a duplicate of a builtin type is handled as expected.
+ */
+static void test_add_type_c(void)
+{
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* dvf = NULL;
+ etch_type* duptype = NULL;
+ etch_type* efftype = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ duptype = new_type(str_etch_runtime_exception);
+
+ /* when try to add a duplicate, vf returns original and destroys duplicate */
+ efftype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->add_type(dvf, duptype);
+ CU_ASSERT_PTR_EQUAL(efftype, builtins._mt__etch_runtime_exception);
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_get_type_by_id()
+ * test that a type can be retrived by its id, and memory accounted for.
+ */
+static void test_get_type_by_id(void)
+{
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* dvf = NULL;
+ /* note that a *reference* is returned (not disposable), not a copy. */
+ unsigned saved_id = 0;
+ etch_type* newtype = NULL;
+ etch_type* foundtype = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ newtype = new_type(L"testtype");
+ saved_id = newtype->id;
+ ((struct i_value_factory*)((etch_object*)dvf)->vtab)->add_type(dvf, newtype);
+
+ foundtype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_id(dvf, saved_id);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(foundtype);
+ CU_ASSERT_EQUAL(saved_id, foundtype->id);
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ etch_object_destroy(newtype); /* vf now owns its nonstatic user types */
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_get_type_by_name()
+ * test that a type can be retrived by its name, and memory accounted for.
+ */
+static void test_get_type_by_name(void)
+{
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* dvf = NULL;
+ /* note that a *reference* is returned (not disposable), not a copy. */
+ etch_type* newtype = NULL;
+ etch_type* foundtype = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ newtype = new_type(L"testtype");
+ ((struct i_value_factory*)((etch_object*)dvf)->vtab)->add_type(dvf, newtype);
+
+ foundtype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_name(dvf, L"testtype");
+ CU_ASSERT_PTR_NOT_NULL_FATAL(foundtype);
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ etch_object_destroy(newtype); /* vf now owns its nonstatic user types */
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_get_types()
+ * test that all types can be retrived as a list, and memory accounted for.
+ */
+static void test_get_types(void)
+{
+ int result;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* dvf = NULL;
+ etch_hashtable* map = NULL;
+ etch_iterator iterator;
+ etch_arraylist* typeslist = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* thisitem = &hashbucket;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ // get type map
+ map = dvf->types;
+ // get type list
+ typeslist = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_types(dvf);
+
+ set_iterator(&iterator, typeslist, &typeslist->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ /* we're iterating the arraylist, so we're looking at the value,
+ * which of course was the hashtable key */
+ etch_type* thistype = (etch_type*) iterator.current_value;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thistype);
+
+ /* map.remove() sends us back the key and value if we ask for it. so we
+ * are using remove both to look up the type, and to remove it so we can
+ * verify that the list content is one to one with the types map */
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->removeh
+ (map->realtable, ((etch_object*)thistype)->get_hashkey(thistype), map, (void**)&thisitem);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = etchmap_count(map);
+ iterator.next(&iterator);
+ }
+
+ /* assert that list contnent and map content were the same */
+ result = etchmap_count(map);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* the list is marked to not free content, which is what we want, however
+ * if would not make a difference if it were not, since the types are refs
+ * to the builtin types, thus marked static, and so were the list to invoke
+ * destructors on its content, the destructor would take no action.
+ */
+ etch_object_destroy(typeslist);
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_authxcp_fields()
+ * test that auth exception type is configured as expected
+ */
+static void test_authxcp_fields(void)
+{
+ int result = 0;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* dvf = NULL;
+ etch_type* msgtype = NULL;
+ etch_field* msgkey1 = NULL;
+ etch_field* msgkey2 = NULL;
+ etch_object* val_good = NULL;
+ etch_object* val_unxp = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ /* apparently the purpose of this test, ported from java test, is to test that
+ * the authxcp type has a _mf_msg field, and that if we then make a message
+ * of type authxcp and put to it a string value keyed by _mf_msg, that it
+ * validates OK. i.e., the _mf_msg field of a authxcp should be a string.
+ */
+ msgtype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_name(dvf, str_etch_auth_exception);
+ msgkey1 = clone_field(builtins._mf_msg);
+ msgkey2 = clone_field(builtins._mf_msg);
+ val_good = (etch_object*) new_stringw(L"dlrow olleh");
+ val_unxp = (etch_object*) new_int32(0);
+
+ result = do_test_type_field(dvf, msgtype, msgkey1, val_good);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = do_test_type_field(dvf, msgtype, msgkey2, val_unxp);
+ CU_ASSERT_EQUAL(result, -1); /* type validation should fail */
+
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_rtxcp_fields()
+ * test that runtime exception type is configured as expected
+ */
+static void test_rtxcp_fields(void)
+{
+ int result = 0;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* dvf = NULL;
+ etch_type* msgtype = NULL;
+ etch_field* msgkey1 = NULL;
+ etch_field* msgkey2 = NULL;
+ etch_object* val_good = NULL;
+ etch_object* val_unxp = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ msgtype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_name(dvf, str_etch_runtime_exception);
+ msgkey1 = clone_field(builtins._mf_msg);
+ msgkey2 = clone_field(builtins._mf_msg);
+ val_good = (etch_object*) new_stringw(L"dlrow olleh");
+ val_unxp = (etch_object*) new_int32(0);
+
+ result = do_test_type_field(dvf, msgtype, msgkey1, val_good);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = do_test_type_field(dvf, msgtype, msgkey2, val_unxp);
+ CU_ASSERT_EQUAL(result, -1); /* type validation should fail */
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_exception_fields()
+ * test that exception type is configured as expected
+ */
+static void test_exception_fields(void)
+{
+ int result = 0;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* dvf = NULL;
+ etch_type* msgtype = NULL;
+ etch_field* key_result = NULL;
+ etch_field* key_msg_id = NULL;
+ etch_field* key_inrepto = NULL;
+ etch_object* val_rtexp = NULL;
+ etch_object* val_long1 = NULL;
+ etch_object* val_long2 = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ msgtype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_name(dvf, str_exception);
+ key_result = clone_field(builtins._mf_result);
+ key_msg_id = clone_field(builtins._mf__message_id);
+ key_inrepto = clone_field(builtins._mf__in_reply_to);
+ val_rtexp = (etch_object*) new_etch_exception_from_errorcode(ETCH_ERROR);
+ val_long1 = (etch_object*) new_int64(123);
+ val_long2 = (etch_object*) new_int64(456);
+
+ result = do_test_type_field(dvf, msgtype, key_result, val_rtexp);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = do_test_type_field(dvf, msgtype, key_msg_id, val_long1);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = do_test_type_field(dvf, msgtype, key_inrepto, val_long2);
+ CU_ASSERT_EQUAL(result, 0);
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_get_string_encoding()
+ */
+static void test_get_string_encoding(void)
+{
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ default_value_factory* dvf = NULL;
+ wchar_t* encoding = NULL;
+ etch_string* newstring = NULL;
+ int result;
+ int etch_encoding;
+ char* teststring = "8 bit characters";
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ /* note that a reference to a raw string is returned */
+ encoding = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_string_encoding(dvf);
+
+ result = wcscmp(encoding, L"utf-8");
+ CU_ASSERT_EQUAL(result, 0);
+
+ etch_encoding = get_etch_string_encoding((etch_value_factory*)dvf);
+
+ newstring = new_string("8 bit characters", etch_encoding);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newstring);
+ result = strcmp(newstring->v.valc, teststring);
+ CU_ASSERT_EQUAL(result, 0);
+
+ etch_object_destroy(newstring);
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_add_mixin()
+ */
+static void test_add_mixin(void)
+{
+ int result = 0;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ default_value_factory* mixvf1_vf = NULL;
+ vf_idname_map* mixvf1_typemap = NULL;
+ class_to_type_map* mixvf1_c2tmap = NULL;
+
+ default_value_factory* mixvf2_vf = NULL;
+ vf_idname_map* mixvf2_typemap = NULL;
+ class_to_type_map* mixvf2_c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ // init statics
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ mixvf1_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(mixvf1_typemap);
+ mixvf1_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(mixvf1_c2tmap);
+ // init statics
+ defvf_initialize_static(mixvf1_typemap, mixvf1_c2tmap);
+ mixvf1_vf = new_default_value_factory(mixvf1_typemap, mixvf1_c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(mixvf1_vf);
+
+ mixvf2_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(mixvf2_typemap);
+ mixvf2_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(mixvf2_c2tmap);
+ // init statics
+ defvf_initialize_static(mixvf2_typemap, mixvf2_c2tmap);
+ mixvf2_vf = new_default_value_factory(mixvf2_typemap, mixvf2_c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(mixvf2_vf);
+
+ result = defvf_add_mixin(dvf, (etch_value_factory*) mixvf1_vf);
+ CU_ASSERT_EQUAL(result, 0);
+ result = defvf_add_mixin(dvf, (etch_value_factory*) mixvf2_vf);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = dvf->mixins->count;
+ CU_ASSERT_EQUAL(result, 2);
+
+ // destroy dvf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // destroy mixvf1_vf
+ etch_object_destroy(mixvf1_vf);
+ etch_object_destroy(mixvf1_c2tmap);
+ etch_object_destroy(mixvf1_typemap);
+
+ // destroy mixvf2_vf
+ etch_object_destroy(mixvf2_vf);
+ etch_object_destroy(mixvf2_c2tmap);
+ etch_object_destroy(mixvf2_typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_mixin_recursion
+ * exercise the various vf apis which are mixin-recursive
+ */
+static void test_mixin_recursion(void)
+{
+ int result = 0;
+ etch_type* rettype = NULL;
+ wchar_t* str_foo = L"foo", *str_bar = L"bar", *str_bogus = L"wtf";
+ etch_type* newtype1 = NULL;
+ etch_type* newtype2 = NULL;
+
+
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ default_value_factory* mixin1_dvf = NULL;
+ vf_idname_map* mixin1_typemap = NULL;
+ class_to_type_map* mixin1_c2tmap = NULL;
+
+ default_value_factory* mixin2_dvf = NULL;
+ vf_idname_map* mixin2_typemap = NULL;
+ class_to_type_map* mixin2_c2tmap = NULL;
+
+ // main vf
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ // mixin1 vf
+ mixin1_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(mixin1_typemap);
+ mixin1_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(mixin1_c2tmap);
+ defvf_initialize_static(mixin1_typemap, mixin1_c2tmap);
+ mixin1_dvf = new_default_value_factory(mixin1_typemap, mixin1_c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(mixin1_dvf);
+
+ // mixin2 vf
+ mixin2_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(mixin2_typemap);
+ mixin2_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(mixin2_c2tmap);
+ defvf_initialize_static(mixin2_typemap, mixin2_c2tmap);
+ mixin2_dvf = new_default_value_factory(mixin2_typemap, mixin2_c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(mixin2_dvf);
+
+ newtype1 = new_static_type(str_foo);
+ newtype2 = new_static_type(str_bar);
+
+ rettype = ((struct i_value_factory*)((etch_object*)mixin1_dvf)->vtab)->add_type(mixin1_dvf, newtype1);
+ CU_ASSERT_PTR_EQUAL_FATAL(rettype, newtype1);
+
+ rettype = ((struct i_value_factory*)((etch_object*)mixin2_dvf)->vtab)->add_type(mixin2_dvf, newtype2);
+ CU_ASSERT_PTR_EQUAL_FATAL(rettype, newtype2);
+
+ result = defvf_add_mixin(dvf, (etch_value_factory*) mixin1_dvf);
+ CU_ASSERT_EQUAL(result, 0);
+ result = defvf_add_mixin(dvf, (etch_value_factory*) mixin2_dvf);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = defvf_add_mixin(mixin1_dvf, (etch_value_factory*) mixin2_dvf);
+ CU_ASSERT_EQUAL(result, 0);
+
+ rettype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_name(dvf, str_exception);
+ CU_ASSERT_PTR_NOT_NULL(rettype); /* builtin type */
+
+ rettype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_name(mixin1_dvf, str_exception);
+ CU_ASSERT_PTR_NOT_NULL(rettype); /* builtin type */
+
+ rettype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_name(mixin2_dvf, str_exception);
+ CU_ASSERT_PTR_NOT_NULL(rettype); /* builtin type */
+
+ /* mixin recursive by-name lookup */
+ rettype = ((struct i_value_factory*)((etch_object*)mixin1_dvf)->vtab)->get_type_by_name(mixin1_dvf, str_bar);
+ CU_ASSERT_PTR_NOT_NULL(rettype); /* via mixvf1 mixin 1 */
+
+ rettype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_name(dvf, str_foo);
+ CU_ASSERT_PTR_NOT_NULL(rettype); /* via vf mixin 1 */
+
+ rettype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_name(dvf, str_bar);
+ CU_ASSERT_PTR_NOT_NULL(rettype); /* via vf mixin 2 */
+
+ /* mixin recursive by-id lookup */
+ rettype = ((struct i_value_factory*)((etch_object*)mixin1_dvf)->vtab)->get_type_by_id(mixin1_dvf, newtype2->id);
+ CU_ASSERT_PTR_NOT_NULL(rettype); /* via mixvf1 mixin 1 */
+
+ rettype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_id(dvf, newtype1->id);
+ CU_ASSERT_PTR_NOT_NULL(rettype); /* via vf mixin 1 */
+
+ rettype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_id(dvf, newtype2->id);
+ CU_ASSERT_PTR_NOT_NULL(rettype); /* via vf mixin 2 */
+
+ /* negative tests will recurse all mixins */
+ rettype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_name(mixin1_dvf, str_bogus);
+ CU_ASSERT_PTR_NULL(rettype);
+
+ rettype = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_type_by_id(dvf, compute_id_name_id_from_widename(str_bogus));
+ CU_ASSERT_PTR_NULL(rettype); /* via vf mixin 1 */
+
+ // destroy mixvf2_vf
+ etch_object_destroy(mixin2_dvf);
+ etch_object_destroy(mixin2_c2tmap);
+ etch_object_destroy(mixin2_typemap);
+
+ // destroy mixvf1_vf
+ etch_object_destroy(mixin1_dvf);
+ etch_object_destroy(mixin1_c2tmap);
+ etch_object_destroy(mixin1_typemap);
+
+ // destroy mixvf2_vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ destroy_static_type(newtype1);
+ destroy_static_type(newtype2);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_custom_struct_type
+ *
+ */
+static void test_custom_struct_type(void)
+{
+ int result = 0;
+ etch_type* rettype = NULL;
+ wchar_t *str_vf0 = L"vf0", *str_vf1 = L"vf1", *str_vf2 = L"vf2";
+
+ default_value_factory* vf0 = NULL;
+ vf_idname_map* vf0_typemap = NULL;
+ class_to_type_map* vf0_c2tmap = NULL;
+
+ default_value_factory* vf1 = NULL;
+ vf_idname_map* vf1_typemap = NULL;
+ class_to_type_map* vf1_c2tmap = NULL;
+
+ default_value_factory* vf2 = NULL;
+ vf_idname_map* vf2_typemap = NULL;
+ class_to_type_map* vf2_c2tmap = NULL;
+
+ etch_type* cstype0 = new_static_type(str_vf0);
+ etch_type* cstype1 = new_static_type(str_vf1);
+ etch_type* cstype2 = new_static_type(str_vf2);
+
+ const unsigned short ETCHTYPE_CSCLASS0 = 0xe0, CLASSID_CSCLASS0 = 0xf0;
+ const unsigned short ETCHTYPE_CSCLASS1 = 0xe1, CLASSID_CSCLASS1 = 0xf1;
+ const unsigned short ETCHTYPE_CSCLASS2 = 0xe2, CLASSID_CSCLASS2 = 0xf2;
+ const unsigned short ETCHTYPE_CSBOGUS = 0xe3, CLASSID_BOGUS = 0xf3;
+
+ const unsigned csclass0 = ETCHMAKECLASS(ETCHTYPE_CSCLASS0, CLASSID_CSCLASS0);
+ const unsigned csclass1 = ETCHMAKECLASS(ETCHTYPE_CSCLASS1, CLASSID_CSCLASS1);
+ const unsigned csclass2 = ETCHMAKECLASS(ETCHTYPE_CSCLASS2, CLASSID_CSCLASS2);
+ const unsigned csbogus = ETCHMAKECLASS(ETCHTYPE_CSBOGUS, CLASSID_BOGUS);
+
+ vf0_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(vf0_typemap);
+ vf0_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(vf0_c2tmap);
+ defvf_initialize_static(vf0_typemap, vf0_c2tmap);
+ vf0 = new_default_value_factory(vf0_typemap, vf0_c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf0);
+ result = class_to_type_map_put(vf0->class_to_type, csclass0, cstype0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ vf1_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(vf1_typemap);
+ vf1_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(vf1_c2tmap);
+ defvf_initialize_static(vf1_typemap, vf1_c2tmap);
+ vf1 = new_default_value_factory(vf1_typemap, vf1_c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf1);
+ result = class_to_type_map_put(vf1->class_to_type, csclass1, cstype1);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ vf2_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(vf2_typemap);
+ vf2_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(vf2_c2tmap);
+ defvf_initialize_static(vf2_typemap, vf2_c2tmap);
+ vf2 = new_default_value_factory(vf2_typemap, vf2_c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf2);
+ result = class_to_type_map_put(vf2->class_to_type, csclass2, cstype2);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = defvf_add_mixin(vf0, (etch_value_factory*) vf1);
+ CU_ASSERT_EQUAL(result, 0);
+ result = defvf_add_mixin(vf0, (etch_value_factory*) vf2);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = defvf_add_mixin(vf1, (etch_value_factory*) vf2);
+ CU_ASSERT_EQUAL(result, 0);
+
+ rettype = ((struct i_value_factory*)((etch_object*)vf0)->vtab)->get_custom_struct_type(vf0, csclass0);
+ CU_ASSERT_PTR_EQUAL(rettype, cstype0);
+
+ /* recurse mixed-in vfs for custom types */
+ rettype = ((struct i_value_factory*)((etch_object*)vf0)->vtab)->get_custom_struct_type(vf0, csclass1);
+ CU_ASSERT_PTR_EQUAL(rettype, cstype1);
+
+ rettype = ((struct i_value_factory*)((etch_object*)vf0)->vtab)->get_custom_struct_type(vf0, csclass2);
+ CU_ASSERT_PTR_EQUAL(rettype, cstype2);
+
+ /* negative test will recurse all mixins */
+ rettype = ((struct i_value_factory*)((etch_object*)vf0)->vtab)->get_custom_struct_type(vf0, csbogus);
+ CU_ASSERT_PTR_NULL(rettype);
+
+ // destroy vf2
+ etch_object_destroy(vf2);
+ etch_object_destroy(vf2_c2tmap);
+ etch_object_destroy(vf2_typemap);
+
+ // destroy vf1
+ etch_object_destroy(vf1);
+ etch_object_destroy(vf1_c2tmap);
+ etch_object_destroy(vf1_typemap);
+
+ // destroy vf0
+ etch_object_destroy(vf0);
+ etch_object_destroy(vf0_c2tmap);
+ etch_object_destroy(vf0_typemap);
+
+ destroy_static_type(cstype0);
+ destroy_static_type(cstype1);
+ destroy_static_type(cstype2);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_get_set_message_id()
+ */
+static void test_get_set_message_id(void)
+{
+ int result = 0;
+ int64 idvalue = 12345;
+ etch_type* newtype = new_static_type(L"testtype");
+ etch_int64* idobj = new_int64(idvalue), *retobj = NULL;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_message* msg = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ msg = new_message(newtype, ETCH_DEFSIZE, (etch_value_factory*) dvf);
+
+ etchtype_put_validator(newtype, builtins._mf__message_id, (etch_object*) etchvtor_int64_get(0));
+
+ result = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->set_message_id(dvf, msg, idobj); /* we no longer own idobj */
+ CU_ASSERT_EQUAL_FATAL(result,0); /* ensure idobj validates ok */
+
+ /* we get back a reference to the ID object, not a copy */
+ retobj = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_message_id(dvf, msg);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(retobj);
+ CU_ASSERT_EQUAL(retobj->value, idvalue);
+
+ etch_object_destroy(msg);
+
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ destroy_static_type(newtype);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_get_set_in_reply_to()
+ */
+static void test_get_set_in_reply_to(void)
+{
+ int result = 0;
+ int64 idvalue = 12345;
+ etch_type* newtype = new_static_type(L"testtype");
+ etch_int64* idobj = new_int64(idvalue), *retobj = NULL;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ etch_message* msg = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ msg = new_message(newtype, ETCH_DEFSIZE, (etch_value_factory*) dvf);
+
+ etchtype_put_validator(newtype, builtins._mf__in_reply_to, (etch_object*) etchvtor_int64_get(0));
+
+ result = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->set_in_reply_to(dvf, msg, idobj);
+ CU_ASSERT_EQUAL_FATAL(result,0); /* ensure idobj validates ok */
+
+ /* we get back a reference to the ID, not a copy */
+ retobj = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->get_in_reply_to(dvf, msg);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(retobj);
+ CU_ASSERT_EQUAL(retobj->value, idvalue);
+
+ etch_object_destroy(msg);
+
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ destroy_static_type(newtype);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_export_custom_value_exception()
+ * export an etch_exception
+ */
+static void test_export_custom_value_exception(void)
+{
+ int result = 0;
+ etch_structvalue* sv = NULL;
+ etch_serializer* impexhelper = NULL;
+ etch_string* exported_stringobj = NULL;
+ etch_exception* nullptr_exception = NULL;
+ wchar_t* exception_string_out = NULL;
+
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ nullptr_exception = new_etch_exception_from_errorcode(ETCH_EINVAL);
+
+ /* retain ownership of nullptr_exception, assume ownership of returned struct */
+ sv = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->export_custom_value(dvf, (etch_object*) nullptr_exception);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ /* we could hard code the struct key for the test, but to be thorough
+ * let's get it programmatically. we get the import-export helper for
+ * the type of object we are exporting (exception). from the helper
+ * will get the key for the expected string export value, which should
+ * be an etch_field of name "msg" */
+ impexhelper = etchtype_get_impexphelper(builtins._mt__exception);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ /* get reference to exported object from struct, expected to be an
+ * etch_string wrapping an ascii string which is the exception text.
+ * the exception text will contain the instance text specified above,
+ * specifically it should be "null pointer exception dlr0w 0ll3h" */
+ exported_stringobj = (etch_string*) structvalue_get(sv, impexhelper->field);
+ result = is_etch_string(exported_stringobj);
+ CU_ASSERT_EQUAL_FATAL(result,TRUE);
+
+ exception_string_out = exported_stringobj->v.valw;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(exception_string_out);
+
+ etch_object_destroy(sv);
+
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_import_custom_value_exception()
+ * import an etch exception type
+ */
+static void test_import_custom_value_exception(void)
+{
+ etch_structvalue* sv = NULL;
+ etch_exception* inexcp = NULL;
+ etch_serializer* impexhelper = NULL;
+ char *outtext = "null pointer exception fubar";
+ wchar_t *expected = L"null pointer exception fubar", *intext = NULL;
+ int result = 0;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ impexhelper = etchtype_get_impexphelper(builtins._mt__exception);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ sv = new_structvalue(builtins._mt__exception, 1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ result = structvalue_put(sv, clone_field(impexhelper->field),
+ (etch_object*) new_string(outtext, ETCH_ENCODING_UTF8));
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ /* relinquish ownership of struct, assume ownership of inexcp */
+ /* todo need to serialize exception type perhaps, in order to be able to
+ * re-instantiate an exception as a null pointer exception for example */
+ inexcp = (etch_exception*) ((struct i_value_factory*)((etch_object*)dvf)->vtab)->import_custom_value(dvf, sv);
+ CU_ASSERT_EQUAL_FATAL(is_etch_exception(inexcp), TRUE);
+
+ intext = etch_exception_get_message(inexcp)->v.valw;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(intext);
+ result = wcscmp(intext, expected);
+ CU_ASSERT_EQUAL(result,0);
+
+ etch_object_destroy(inexcp);
+
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_export_custom_value_runtime_exception()
+ * export an etch_runtime_exception
+ */
+static void test_export_custom_value_runtime_exception(void)
+{
+ int result = 0;
+ etch_structvalue* sv = NULL;
+ etch_serializer* impexhelper = NULL;
+ etch_string* exported_stringobj = NULL;
+ etch_exception* runtime_exception = NULL;
+ wchar_t* exception_string_out = NULL;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ runtime_exception = new_etch_exception_from_errorcode(ETCH_ERROR);
+
+ /* retain ownership of runtime_exception, assume ownership of returned struct */
+ sv = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->export_custom_value(dvf, (etch_object*) runtime_exception);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ /* see comments at this spot in test_export_custom_value_exception */
+ impexhelper = etchtype_get_impexphelper(builtins._mt__etch_runtime_exception);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ /* get reference to exported object from struct, expected to be an
+ * etch_string wrapping an ascii string which is the exception text.
+ * the exception text will contain the instance text specified above,
+ * specifically it should be "etch runtime exception tey rabuf ew era" */
+ exported_stringobj = (etch_string*) structvalue_get(sv, impexhelper->field);
+ result = is_etch_string(exported_stringobj);
+ CU_ASSERT_EQUAL_FATAL(result,TRUE);
+
+ exception_string_out = exported_stringobj->v.valw;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(exception_string_out);
+
+
+ etch_object_destroy(sv);
+
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_import_custom_value_runtime_exception()
+ * import an etch rutime exception type
+ */
+static void test_import_custom_value_runtime_exception(void)
+{
+ etch_structvalue* sv = NULL;
+ etch_exception* inexcp = NULL;
+ etch_serializer* impexhelper = NULL;
+ char *outtext = "etch runtime exception fubar";
+ wchar_t *expected = L"etch runtime exception fubar", *intext = NULL;
+ int result = 0;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ impexhelper = etchtype_get_impexphelper(builtins._mt__etch_runtime_exception);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ sv = new_structvalue(builtins._mt__etch_runtime_exception, 1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ result = structvalue_put(sv, clone_field(impexhelper->field),
+ (etch_object*) new_string(outtext, ETCH_ENCODING_UTF8));
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ /* relinquish ownership of struct, assume ownership of inexcp */
+ inexcp = (etch_exception*) ((struct i_value_factory*)((etch_object*)dvf)->vtab)->import_custom_value(dvf, sv);
+ CU_ASSERT_EQUAL_FATAL(is_etch_exception(inexcp), TRUE);
+
+ intext = etch_exception_get_message(inexcp)->v.valw;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(intext);
+ result = wcscmp(intext, expected);
+ CU_ASSERT_EQUAL(result,0);
+
+ etch_object_destroy(inexcp);
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_export_custom_value_list()
+ * export an etch list type
+ */
+static void test_export_custom_value_list(void)
+{
+ int result = 0;
+ etch_structvalue* sv = NULL;
+ etch_arraylist* outlist = NULL;
+ etch_nativearray* explist = NULL;
+ etch_int32* content = NULL;
+ etch_serializer* impexhelper = NULL;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ outlist = new_etch_arraylist(0,0);
+ outlist->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
+ outlist->is_readonly = FALSE;
+ outlist->content_obj_type = ETCHTYPEB_PRIMITIVE;
+ outlist->content_class_id = CLASSID_PRIMITIVE_INT32;
+ etch_arraylist_add(outlist, new_int32(1));
+ etch_arraylist_add(outlist, new_int32(2));
+
+ /* retain ownership of outlist, assume ownership of returned struct */
+ sv = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->export_custom_value(dvf, (etch_object*) outlist);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ /* see comments at this spot in test_export_custom_value_exception() */
+ impexhelper = etchtype_get_impexphelper(builtins._mt__etch_list);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ explist = (etch_nativearray*) structvalue_get(sv, impexhelper->field);
+
+ result = is_etch_nativearray(explist);
+ CU_ASSERT_EQUAL_FATAL(result,TRUE);
+ CU_ASSERT_EQUAL(explist->dimension[0],2);
+
+ explist->get1(explist, &content, 0);
+ CU_ASSERT_EQUAL_FATAL(is_etch_int32(content), TRUE);
+ CU_ASSERT_EQUAL(content->value, 1);
+ content = NULL;
+ explist->get1(explist, &content, 1);
+ CU_ASSERT_EQUAL_FATAL(is_etch_int32(content), TRUE);
+ CU_ASSERT_EQUAL(content->value, 2);
+
+ etch_object_destroy(outlist);
+ etch_object_destroy(sv);
+
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_import_custom_value_list()
+ * import an etch list type
+ */
+static void test_import_custom_value_list(void)
+{
+ etch_iterator iterator;
+ etch_structvalue* sv = NULL;
+ etch_arraylist* inlist = NULL;
+ etch_arrayvalue* outlist = NULL;
+ etch_serializer* impexhelper = NULL;
+ int result = 0;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ impexhelper = etchtype_get_impexphelper(builtins._mt__etch_list);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ sv = new_structvalue(builtins._mt__etch_list, 1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ outlist = new_arrayvalue_default();
+ arrayvalue_add(outlist, (ETCH_ARRAY_ELEMENT*)new_int32(1));
+ arrayvalue_add(outlist, (ETCH_ARRAY_ELEMENT*)new_int32(2));
+
+ result = structvalue_put(sv, clone_field(impexhelper->field), (etch_object*) outlist);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ /* relinquish ownership of struct and outlist, assume ownership of inlist */
+ inlist = (etch_arraylist*) ((struct i_value_factory*)((etch_object*)dvf)->vtab)->import_custom_value(dvf, sv);
+ CU_ASSERT_EQUAL_FATAL(is_etch_arraylist(inlist), TRUE);
+
+ set_iterator(&iterator, inlist, &inlist->iterable);
+
+ /* verify that imported list content is same as original */
+ while(iterator.has_next(&iterator))
+ {
+ etch_int32* intobj = iterator.current_value;
+ CU_ASSERT_EQUAL_FATAL(is_etch_int32(intobj), TRUE);
+ switch(intobj->value) { case 1: case 2: break; default: result = -1; }
+ CU_ASSERT_NOT_EQUAL(result, -1);
+ iterator.next(&iterator);
+ }
+
+ etch_object_destroy(inlist);
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_export_custom_value_map()
+ * export an etch map type
+ */
+static void test_export_custom_value_map(void)
+{
+ etch_structvalue* sv = NULL;
+ etch_hashtable* outmap = NULL;
+ etch_nativearray* explist = NULL;
+ etch_int32* icontent = NULL;
+ etch_string* scontent = NULL;
+ etch_serializer* impexhelper = NULL;
+ int result = 0, outmapcount = 0;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ outmap = new_etch_map(0); /* etch_map is an object-to-object hashtable */
+ etchmap_map_add (outmap, (etch_object*) new_string("1", ETCH_ENCODING_ASCII), (etch_object*) new_int32(1));
+ etchmap_map_add (outmap, (etch_object*) new_string("2", ETCH_ENCODING_ASCII), (etch_object*) new_int32(2));
+ outmapcount = etchmap_count(outmap);
+ CU_ASSERT_EQUAL_FATAL(outmapcount, 2);
+
+ /* retain ownership of outmap, assume ownership of returned struct */
+ sv = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->export_custom_value(dvf, (etch_object*) outmap);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ /* see comments at this spot in test_export_custom_value_exception() */
+ impexhelper = etchtype_get_impexphelper(builtins._mt__etch_map);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ explist = (etch_nativearray*) structvalue_get(sv, impexhelper->field);
+
+ result = is_etch_nativearray(explist);
+ CU_ASSERT_EQUAL_FATAL(result,TRUE);
+ CU_ASSERT_EQUAL(explist->dimension[0],4);
+
+ explist->get1(explist, &scontent, 0);
+ CU_ASSERT_EQUAL_FATAL(is_etch_string(scontent), TRUE);
+ icontent = NULL;
+ scontent = NULL;
+ explist->get1(explist, &icontent, 1);
+ CU_ASSERT_EQUAL_FATAL(is_etch_int32(icontent), TRUE);
+ CU_ASSERT_EQUAL(icontent->value, 1);
+ icontent = NULL;
+ scontent = NULL;
+
+
+ explist->get1(explist, &scontent, 2);
+ CU_ASSERT_EQUAL_FATAL(is_etch_string(scontent), TRUE);
+ icontent = NULL;
+ scontent = NULL;
+ explist->get1(explist, &icontent, 3);
+ CU_ASSERT_EQUAL_FATAL(is_etch_int32(icontent), TRUE);
+ CU_ASSERT_EQUAL(icontent->value, 2);
+ icontent = NULL;
+ scontent = NULL;
+
+ etch_object_destroy(outmap);
+ etch_object_destroy(sv);
+
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins(
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_import_custom_value_map()
+ * import an etch map type
+ */
+static void test_import_custom_value_map(void)
+{
+ etch_iterator iterator;
+ etch_structvalue* sv = NULL;
+ etch_hashtable* inmap = NULL;
+ etch_arrayvalue* outlist = NULL;
+ etch_serializer* impexhelper = NULL;
+ int result = 0;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ impexhelper = etchtype_get_impexphelper(builtins._mt__etch_map);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ sv = new_structvalue(builtins._mt__etch_map, 1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ outlist = new_arrayvalue_default();
+ arrayvalue_add(outlist, (ETCH_ARRAY_ELEMENT*)new_string("1", ETCH_ENCODING_ASCII));
+ arrayvalue_add(outlist, (ETCH_ARRAY_ELEMENT*)new_int32(1));
+ arrayvalue_add(outlist, (ETCH_ARRAY_ELEMENT*)new_string("2", ETCH_ENCODING_ASCII));
+ arrayvalue_add(outlist, (ETCH_ARRAY_ELEMENT*)new_int32(2));
+
+ result = structvalue_put(sv, clone_field(impexhelper->field), (etch_object*) outlist);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ /* relinquish ownership of struct and outlist, assume ownership of inmap */
+ inmap = (etch_hashtable*) ((struct i_value_factory*)((etch_object*)dvf)->vtab)->import_custom_value(dvf, sv);
+ CU_ASSERT_EQUAL_FATAL(is_etch_hashtable(inmap), TRUE);
+
+ set_iterator(&iterator, inmap, &inmap->iterable);
+
+ /* verify that imported map content is same as original */
+ while(iterator.has_next(&iterator))
+ {
+ etch_string* keyobj = iterator.current_key;
+ etch_int32* valobj = iterator.current_value;
+ CU_ASSERT_EQUAL_FATAL(is_etch_string(keyobj), TRUE);
+ CU_ASSERT_EQUAL_FATAL(is_etch_int32(valobj), TRUE);
+ switch(valobj->value) { case 1: case 2: break; default: result = -1; }
+ CU_ASSERT_NOT_EQUAL(result, -1);
+
+ iterator.next(&iterator);
+ }
+
+ etch_object_destroy(inmap);
+
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+/**
+ * test_export_custom_value_set()
+ * export an etch set type
+ */
+static void test_export_custom_value_set(void)
+{
+ etch_structvalue* sv = NULL;
+ etch_string* content = NULL;
+ etch_hashtable* outset = NULL;
+ etch_nativearray* explist = NULL;
+ etch_serializer* impexhelper = NULL;
+ int result = 0, outsetcount = 0;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ outset = new_etch_set(0); /* etch_set is an object-to-null hashtable */
+ etchmap_set_add (outset, (etch_object*) new_string("1", ETCH_ENCODING_ASCII));
+ etchmap_set_add (outset, (etch_object*) new_string("2", ETCH_ENCODING_ASCII));
+ outsetcount = etchmap_count(outset);
+ CU_ASSERT_EQUAL_FATAL(outsetcount, 2);
+
+ /* retain ownership of outset, assume ownership of returned struct */
+ sv = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->export_custom_value(dvf, (etch_object*) outset);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ /* see comments at this spot in test_export_custom_value_exception() */
+ impexhelper = etchtype_get_impexphelper(builtins._mt__etch_set);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ explist = (etch_nativearray*) structvalue_get(sv, impexhelper->field);
+
+ result = is_etch_nativearray(explist);
+ CU_ASSERT_EQUAL_FATAL(result,TRUE);
+ CU_ASSERT_EQUAL(explist->dimension[0],2);
+
+ explist->get1(explist, &content, 0);
+ CU_ASSERT_EQUAL_FATAL(is_etch_string(content), TRUE);
+ content = NULL;
+ explist->get1(explist, &content, 1);
+ CU_ASSERT_EQUAL_FATAL(is_etch_string(content), TRUE);
+ content = NULL;
+
+ etch_object_destroy(outset);
+ etch_object_destroy(sv);
+
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_import_custom_value_set()
+ * import an etch set type
+ */
+static void test_import_custom_value_set(void)
+{
+ etch_iterator iterator;
+ etch_structvalue* sv = NULL;
+ etch_hashtable* inset = NULL;
+ etch_arrayvalue* outlist = NULL;
+ etch_serializer* impexhelper = NULL;
+ int result = 0;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ impexhelper = etchtype_get_impexphelper(builtins._mt__etch_set);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ sv = new_structvalue(builtins._mt__etch_set, 1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ outlist = new_arrayvalue_default();
+ arrayvalue_add(outlist, (ETCH_ARRAY_ELEMENT*)new_string("1", ETCH_ENCODING_ASCII));
+ arrayvalue_add(outlist, (ETCH_ARRAY_ELEMENT*)new_string("2", ETCH_ENCODING_ASCII));
+
+ result = structvalue_put(sv, clone_field(impexhelper->field), (etch_object*) outlist);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ /* relinquish ownership of struct and outlist, assume ownership of inset */
+ inset = (etch_hashtable*) ((struct i_value_factory*)((etch_object*)dvf)->vtab)->import_custom_value(dvf, sv);
+ CU_ASSERT_EQUAL_FATAL(is_etch_set(inset), TRUE);
+
+ set_iterator(&iterator, inset, &inset->iterable);
+
+ /* verify that imported map content is same as original */
+ while(iterator.has_next(&iterator))
+ {
+ etch_string* setobj = iterator.current_key;
+ CU_ASSERT_EQUAL(is_etch_string(setobj), TRUE);
+ CU_ASSERT_PTR_NULL(iterator.current_value);
+ iterator.next(&iterator);
+ }
+
+ etch_object_destroy(inset);
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_export_custom_value_date()
+ * export an etch date type
+ */
+static void test_export_custom_value_date(void)
+{
+ etch_date* outdate = NULL;
+ etch_int64* expdate = NULL;
+ etch_structvalue* sv = NULL;
+ etch_serializer* impexhelper = NULL;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ outdate = new_date();
+
+ /* retain ownership of outdate, assume ownership of returned struct */
+ sv = ((struct i_value_factory*)((etch_object*)dvf)->vtab)->export_custom_value(dvf, (etch_object*) outdate);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ /* see comments at this spot in test_export_custom_value_exception() */
+ impexhelper = etchtype_get_impexphelper(builtins._mt__etch_datetime);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ /* get reference to exported etch_int64 object from struct */
+ expdate = (etch_int64*) structvalue_get(sv, impexhelper->field);
+ CU_ASSERT_EQUAL_FATAL(is_etch_int64(expdate), TRUE);
+
+ etch_object_destroy(outdate);
+ etch_object_destroy(sv);
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_import_custom_value_date()
+ * import an etch date type
+ */
+static void test_import_custom_value_date(void)
+{
+ etch_date* indate = NULL;
+ etch_structvalue* sv = NULL;
+ etch_serializer* impexhelper = NULL;
+ time_t outtime, intime;
+ int result = 0;
+ default_value_factory* dvf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ dvf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(dvf);
+
+ time(&outtime);
+
+ impexhelper = etchtype_get_impexphelper(builtins._mt__etch_datetime);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impexhelper);
+
+ sv = new_structvalue(builtins._mt__etch_datetime, 1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ result = structvalue_put(sv, clone_field(impexhelper->field), (etch_object*) new_int64(outtime));
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ /* relinquish ownership of struct, assume ownership of indate */
+ indate = (etch_date*) ((struct i_value_factory*)((etch_object*)dvf)->vtab)->import_custom_value(dvf, sv);
+ CU_ASSERT_EQUAL_FATAL(is_etch_date(indate), TRUE);
+
+ intime = (time_t) indate->value;
+ CU_ASSERT_EQUAL(intime, outtime);
+
+ etch_object_destroy(indate);
+ // destroy vf
+ etch_object_destroy(dvf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_defvaluefact_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("suite_defvf", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test constructor", test_constructor);
+ CU_add_test(pSuite, "test builtins", test_builtin_types);
+ CU_add_test(pSuite, "test add type a", test_add_type_a);
+ CU_add_test(pSuite, "test add type b", test_add_type_b);
+ CU_add_test(pSuite, "test add type c", test_add_type_c);
+ CU_add_test(pSuite, "test get type by id", test_get_type_by_id);
+ CU_add_test(pSuite, "test get type by name", test_get_type_by_name);
+ CU_add_test(pSuite, "test get all types", test_get_types);
+ CU_add_test(pSuite, "test auth excp fields", test_authxcp_fields);
+ CU_add_test(pSuite, "test runtime excp fields", test_rtxcp_fields);
+ CU_add_test(pSuite, "test get_set_message_id", test_get_set_message_id);
+ CU_add_test(pSuite, "test get_set_in_reply_to", test_get_set_in_reply_to);
+ CU_add_test(pSuite, "test exception fields", test_exception_fields);
+ CU_add_test(pSuite, "test get encoding", test_get_string_encoding);
+
+ CU_add_test(pSuite, "test add mixin", test_add_mixin);
+ CU_add_test(pSuite, "test mixin recursion", test_mixin_recursion);
+ CU_add_test(pSuite, "test custom struct type", test_custom_struct_type);
+
+ CU_add_test(pSuite, "test export custom (exception)", test_export_custom_value_exception);
+ CU_add_test(pSuite, "test export custom (runtime)", test_export_custom_value_runtime_exception);
+ CU_add_test(pSuite, "test export custom (list)", test_export_custom_value_list);
+ CU_add_test(pSuite, "test export custom (map)", test_export_custom_value_map);
+ CU_add_test(pSuite, "test export custom (set)", test_export_custom_value_set);
+ CU_add_test(pSuite, "test export custom (date)", test_export_custom_value_date);
+
+ CU_add_test(pSuite, "test import custom (exception)", test_import_custom_value_exception);
+ CU_add_test(pSuite, "test import custom (runtime)", test_import_custom_value_runtime_exception);
+ CU_add_test(pSuite, "test import custom (list)", test_import_custom_value_list);
+ CU_add_test(pSuite, "test import custom (map)", test_import_custom_value_map);
+ CU_add_test(pSuite, "test import custom (set)", test_import_custom_value_set);
+ CU_add_test(pSuite, "test import custom (date)", test_import_custom_value_date);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/message/test_field.c b/binding-c/runtime/c/src/test/message/test_field.c
new file mode 100644
index 0000000..e7ffdbd
--- /dev/null
+++ b/binding-c/runtime/c/src/test/message/test_field.c
@@ -0,0 +1,207 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_field.c
+ * tests the C implementation of the etch_field object.
+ */
+#include "etch_runtime.h"
+#include "etch_field.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+#include <wchar.h>
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+
+/**
+ * test_field
+ */
+static void test_field(void)
+{
+ int alloc_a = 0, alloc_b = 0, result = 0;
+ etch_field *field1 = NULL, *field2 = NULL;
+
+ const wchar_t* nametext1 = L"abracadabra";
+
+ const wchar_t* nametext2 = L"gilgamesh";
+
+#ifdef ETCH_DEBUGALLOC
+ alloc_a = etch_showmem(0, FALSE);
+#endif
+ field1 = new_field(NULL);
+
+#ifdef ETCH_DEBUGALLOC
+ alloc_b = etch_showmem(0, FALSE);
+#endif
+
+ CU_ASSERT_PTR_NULL(field1);
+ CU_ASSERT_EQUAL(alloc_a, alloc_b);
+
+ field1 = new_field(nametext1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(field1);
+ CU_ASSERT_TRUE(is_good_field(field1));
+
+ field2 = new_field(nametext1);
+ CU_ASSERT_TRUE(is_equal_fields(field1, field2));
+ result = memcmp(field1->name, field2->name, field1->namebytelen);
+ CU_ASSERT_EQUAL(result,0);
+
+ destroy_field(field1);
+ destroy_field(field2);
+
+#ifdef ETCH_DEBUGALLOC
+ alloc_a = etch_showmem(0, FALSE);
+ CU_ASSERT_EQUAL(alloc_a, alloc_b);
+#endif
+
+ field1 = new_field(nametext1);
+ field2 = new_field(nametext2);
+
+ CU_ASSERT_FALSE(is_equal_fields(field1, field2));
+
+ destroy_field(field1);
+ destroy_field(field2);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_inheritance
+ * test that etch_field's inheritance list is as expected
+ */
+static void test_inheritance(void)
+{
+ int ndx = 0, parentcount = 0;
+ etchparentinfo* parentinfo = NULL;
+ const wchar_t* nametext1 = L"abracadabra";
+
+ etch_field *field1 = new_field(nametext1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(field1);
+
+ while(1)
+ { parentinfo = get_next_etch_parent((etch_object*)field1, ndx++);
+ if (NULL == parentinfo) break;
+ parentcount++;
+ #if IS_DEBUG_CONSOLE
+ printf("parent %d type %d class %d\n", parentcount, ((etch_object*)parentinfo)->obj_type, ((etch_object*)parentinfo)->class_id);
+ #endif
+ }
+
+ CU_ASSERT_EQUAL(parentcount, 2); /* field derives from both id_name and object */
+
+ destroy_field(field1);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_field_hashfunc
+ * Unit test field_hashfunc
+ */
+static void test_field_hashfunc(void)
+{
+ unsigned hash1 = 0, hash2 = 0;
+ etch_field *field1 = NULL, *field2 = NULL;
+
+ const wchar_t* nametext1 = L"abracadabra";
+ const size_t numelts1 = wcslen(nametext1);
+ const size_t numbytes1 = sizeof(wchar_t) * ( numelts1 + 1 );
+
+ const wchar_t* nametext2 = L"gilgamesh";
+ const size_t numelts2 = wcslen(nametext2);
+ const size_t numbytes2 = sizeof(wchar_t) * ( numelts2 + 1 );
+
+ hash1 = compute_field_id_from_widename(nametext1);
+ hash2 = compute_field_id_from_widename(nametext2);
+
+ CU_ASSERT_NOT_EQUAL(hash1, hash2);
+
+ field1 = new_field(nametext1);
+ field2 = new_field(nametext2);
+ CU_ASSERT_EQUAL(field1->namebytelen, numbytes1);
+ CU_ASSERT_EQUAL(field2->namebytelen, numbytes2);
+ CU_ASSERT_EQUAL(field1->id, hash1);
+ CU_ASSERT_EQUAL(field2->id, hash2);
+
+ destroy_field(field1);
+ destroy_field(field2);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_field_suite()
+//int main(int argc, char** argv[])
+{
+ CU_pSuite pSuite = CU_add_suite("etch_field tests", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test etch_field constructors/destructors", test_field);
+ CU_add_test(pSuite, "test etch_field hashing", test_field_hashfunc);
+ CU_add_test(pSuite, "test etch_field inheritance", test_inheritance);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/message/test_idname.c b/binding-c/runtime/c/src/test/message/test_idname.c
new file mode 100644
index 0000000..07422c9
--- /dev/null
+++ b/binding-c/runtime/c/src/test/message/test_idname.c
@@ -0,0 +1,165 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_id_name.c
+ * tests the C implementation of the etch_id_name object.
+ */
+#include "etch_runtime.h"
+#include "etch_id_name.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+#include <wchar.h>
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+
+/**
+ * test_id_name
+ */
+static void test_id_name(void)
+{
+ int result = 0;
+ etch_id_name *id_name1 = NULL, *id_name2 = NULL;
+
+ const wchar_t* nametext1 = L"abracadabra";
+
+ const wchar_t* nametext2 = L"gilgamesh";
+
+#ifdef ETCH_DEBUGALLOC
+ alloc_a = etch_showmem(0, FALSE);
+#endif
+ id_name1 = new_id_name(NULL);
+ CU_ASSERT_PTR_NULL(id_name1);
+
+#ifdef ETCH_DEBUGALLOC
+ alloc_b = etch_showmem(0, FALSE);
+ CU_ASSERT_EQUAL(alloc_a, alloc_b);
+#endif
+
+ id_name1 = new_id_name(nametext1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(id_name1);
+ CU_ASSERT_TRUE(is_good_id_name(id_name1));
+
+ id_name2 = new_id_name(nametext1);
+ CU_ASSERT_TRUE(is_equal_id_names(id_name1, id_name2));
+ result = memcmp(id_name1->name, id_name2->name, id_name1->namebytelen);
+ CU_ASSERT_EQUAL(result,0);
+
+ destroy_id_name(id_name1);
+ destroy_id_name(id_name2);
+
+#ifdef ETCH_DEBUGALLOC
+ alloc_a = etch_showmem(0, FALSE);
+ CU_ASSERT_EQUAL(alloc_a, alloc_b);
+#endif
+
+ id_name1 = new_id_name(nametext1);
+ id_name2 = new_id_name(nametext2);
+
+ CU_ASSERT_FALSE(is_equal_id_names(id_name1, id_name2));
+
+ destroy_id_name(id_name1);
+ destroy_id_name(id_name2);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_id_name_hashfunc
+ * Unit test id_name_hashfunc
+ */
+static void test_id_name_hashfunc(void)
+{
+ unsigned hash1 = 0, hash2 = 0;
+ etch_id_name *id_name1 = NULL, *id_name2 = NULL;
+
+ const wchar_t* nametext1 = L"abracadabra";
+ const size_t numelts1 = wcslen(nametext1);
+ const size_t numbytes1 = sizeof(wchar_t) * ( numelts1 + 1 );
+
+ const wchar_t* nametext2 = L"gilgamesh";
+ const size_t numelts2 = wcslen(nametext2);
+ const size_t numbytes2 = sizeof(wchar_t) * ( numelts2 + 1 );
+
+ hash1 = compute_id_name_id_from_widename(nametext1);
+ hash2 = compute_id_name_id_from_widename(nametext2);
+
+ CU_ASSERT_NOT_EQUAL(hash1, hash2);
+
+ id_name1 = new_id_name(nametext1);
+ id_name2 = new_id_name(nametext2);
+ CU_ASSERT_EQUAL(id_name1->namebytelen, numbytes1);
+ CU_ASSERT_EQUAL(id_name2->namebytelen, numbytes2);
+ CU_ASSERT_EQUAL(id_name1->id, hash1);
+ CU_ASSERT_EQUAL(id_name2->id, hash2);
+
+ destroy_id_name(id_name1);
+ destroy_id_name(id_name2);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_idname_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("suite_id_name", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test etch_id_name constructors/destructors", test_id_name);
+ CU_add_test(pSuite, "test etch_id_name hashing", test_id_name_hashfunc);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/message/test_message.c b/binding-c/runtime/c/src/test/message/test_message.c
new file mode 100644
index 0000000..20c11e8
--- /dev/null
+++ b/binding-c/runtime/c/src/test/message/test_message.c
@@ -0,0 +1,1241 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_message.c -- test etch_message object
+ */
+#include "etch_runtime.h"
+#include "etch_connection.h"
+#include "etch_encoding.h"
+#include "etch_thread.h"
+#include "etch_log.h"
+#include "etch_message.h"
+#include "etch_objecttypes.h"
+#include "etch_exception.h"
+#include "etch_cache.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * individual setup and teardown
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+#if(0)
+#define OBJTYPE_FAKETDI_IMPL ETCHTYPEB_INSTANCEDATA
+#define CLASSID_FAKETDI_IMPL 0xf0
+#define OBJTYPE_FAKETDO_IMPL ETCHTYPEB_INSTANCEDATA
+#define CLASSID_FAKETDO_IMPL 0xf1
+#define OBJTYPE_FAKETDI_VTABLE 0x88
+#define OBJTYPE_FAKETDO_VTABLE 0x89
+#define CLASSID_FAKETDI 0xf6
+#define CLASSID_FAKETDO 0xf7
+#endif
+
+#define OBJTYPE_FAKEVF_IMPL ETCHTYPEB_INSTANCEDATA
+#define CLASSID_FAKEVF_IMPL 0xf2
+#define OBJTYPE_FAKEVF_VTABLE 0x90
+#define OBJTYPE_FAKEVF_IMPL ETCHTYPEB_INSTANCEDATA
+#define OBJTYPE_FAKEVF ETCHTYPEB_VALUEFACTORY
+#define CLASSID_FAKEVF 0xf5
+
+#define EXCPTEST_UNCHECKED_STATICTEXT 1
+#define EXCPTEST_CHECKED_COPYTEXT 2
+#define EXCPTEST_CHECKED_STATICTEXT 3
+
+
+typedef struct testitems
+{
+ etch_type* mt1;
+ etch_type* mt2;
+ etch_type* rmt;
+ etch_field* mf1;
+ etch_field* mf2;
+ etch_field* mf3;
+ etch_field* mf4;
+
+ etch_string* s1;
+ etch_string* s2;
+ etch_int32* n1;
+ etch_int32* n2;
+ etch_int64* l1;
+ etch_int64* l2;
+
+ etch_field *mf_bool_d0_1;
+ etch_field *mf_bool_d0_2;
+ etch_field *mf_bool_d1_1;
+ etch_field *mf_bool_d1_2;
+
+ etch_field *mf_int32_d0_1;
+ etch_field *mf_int32_d0_2;
+ etch_field *mf_int32_d1_1;
+ etch_field *mf_int32_d1_2;
+
+ etch_field *mf_str_d0_1;
+ etch_field *mf_str_d0_2;
+ etch_field *mf_str_d1_1;
+ etch_field *mf_str_d1_2;
+
+ etch_field *mf_message_id;
+ etch_field *mf_in_reply_to;
+
+ etch_hashtable* items;
+
+} testitems;
+
+enum objtyp {
+ ETCHTYPE_VTABLE_FAKETDI = 0xfa,
+ ETCHTYPE_VTABLE_FAKETDO = 0xfb,
+ ETCHTYPE_VTABLE_FAKEVF = 0xfc
+};
+
+
+/* = = = = = = = = = = = = = = =
+ * VALUE FACTORY IMPLEMENTATION
+ * = = = = = = = = = = = = = = =
+ */
+
+/**
+ * fake_vf_impl
+ * instance data for a value factory implementation
+ */
+typedef struct fake_vf_impl
+{
+ etch_object object;
+
+ etch_field* mf_message_id;
+ etch_field* mf_in_reply_to;
+
+} fake_vf_impl;
+
+
+/**
+ * destroy_fake_vf_impl
+ * destructor for fake_vf_impl
+ */
+static int destroy_fake_vf_impl(void* data)
+{
+ fake_vf_impl* impl = (fake_vf_impl*)data;
+ int result = verify_object((etch_object*)impl, OBJTYPE_FAKEVF_IMPL, CLASSID_FAKEVF_IMPL, 0);
+ if (result == -1) return -1; /* object passed was not expected object */
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impl->mf_message_id);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impl->mf_in_reply_to);
+
+ etch_object_destroy(impl->mf_message_id);
+ etch_object_destroy(impl->mf_in_reply_to);
+
+ etch_free(impl);
+ return 0;
+}
+
+
+/**
+ * new_fake_vf_impl()
+ * constructor for TDI implementation instance data
+ */
+static fake_vf_impl* new_fake_vf_impl(testitems* items)
+{
+ fake_vf_impl* data = etch_malloc(sizeof(fake_vf_impl), ETCHTYPEB_INSTANCEDATA);
+ memset(data, 0, sizeof(fake_vf_impl));
+ ((etch_object*)data)->obj_type = OBJTYPE_FAKEVF_IMPL;
+ ((etch_object*)data)->class_id = CLASSID_FAKEVF_IMPL;
+
+ data->mf_message_id = (etch_field*)etch_object_clone_func(items->mf_message_id);
+ data->mf_in_reply_to = (etch_field*)etch_object_clone_func(items->mf_in_reply_to);
+
+ ((etch_object*)data)->destroy = destroy_fake_vf_impl;
+ return data;
+}
+
+
+/*
+ * value factory virtual function overrides
+ */
+
+/**
+ * fvf_get_message_id() -- valuefactory.get_message_id() implementation.
+ * @return non-disposable reference to id object, or null if not found.
+ */
+static etch_int64* fvf_get_message_id (void* vfData, etch_message* msg)
+{
+ etch_value_factory* vf = (etch_value_factory*)vfData;
+ etch_int64* id = NULL;
+ fake_vf_impl* data = (fake_vf_impl*) vf->impl;
+
+ id = (etch_int64*) message_get(msg, data->mf_message_id);
+
+ /* return (etch_int64*) verifyx((etch_object*) id, 0,
+ CLASSID_PRIMITIVE_INT64, EXCPTYPE_INTERNALERR); */
+
+ return id;
+}
+
+
+/**
+ * fvf_set_message_id() -- valuefactory.set_message_id() implementation.
+ */
+static int fvf_set_message_id (void* vfData, etch_message* msg, etch_int64* id)
+{
+ etch_value_factory* vf = (etch_value_factory*)vfData;
+ fake_vf_impl* data = (fake_vf_impl*) vf->impl;
+ etch_field* keycopy = (etch_field*)etch_object_clone_func(data->mf_message_id);
+
+ const int result = message_put(msg, keycopy, (etch_object*) id);
+ return result;
+}
+
+
+/**
+ * fvf_get_in_reply_to() -- valuefactory.get_in_reply_to() implementation.
+ */
+static etch_int64* fvf_get_in_reply_to (void* vfData, etch_message* msg)
+{
+ etch_value_factory* vf = (etch_value_factory*)vfData;
+ etch_int64* id = NULL;
+ fake_vf_impl* data = (fake_vf_impl*) vf->impl;
+
+ id = (etch_int64*) message_get(msg, data->mf_in_reply_to);
+
+ return id;
+}
+
+
+/**
+ * fvf_set_in_reply_to() -- valuefactory.set_message_id() implementation.
+ */
+static int fvf_set_in_reply_to (void* vfData, etch_message* msg, etch_int64* id)
+{
+ etch_value_factory* vf = (etch_value_factory*)vfData;
+ fake_vf_impl* data = (fake_vf_impl*) vf->impl;
+ etch_field* keycopy = clone_field(data->mf_in_reply_to);
+
+ /* FYI this copy of the key is put to etch_message* sent message, and gets
+ * freed in msg.destroy(), message owns memory other than vf and type
+ */
+ return message_put(msg, keycopy, (etch_object*) id);
+}
+
+/*
+ * value factory constructors/destructors
+ */
+
+
+#if 0
+/**
+ * fakevf_close() ala java test
+ */
+static void fakevf_close(etch_value_factory* vf)
+{
+ etch_object_destroy(vf);
+}
+#endif
+
+#define CLASSID_FAKE_VALUEFACTORY 200
+
+/**
+ * new_fake_value_factory()
+ * constructor for value factory implementation
+ */
+static etch_value_factory* new_fake_value_factory(testitems* items)
+{
+ etch_value_factory* fakevf = NULL;
+ i_value_factory* vtab = NULL;
+ const int VTABSIZE = sizeof(i_value_factory);
+ const unsigned short CLASS_ID = CLASSID_FAKE_VALUEFACTORY;
+
+ fakevf = new_value_factory(0);
+
+ vtab = etch_cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+ if(!vtab)
+ {
+ vtab = new_vtable(((etch_object*)fakevf)->vtab, VTABSIZE, CLASS_ID);
+
+ /* override four i_value_factory methods */
+ vtab->get_message_id = fvf_get_message_id;
+ vtab->set_message_id = fvf_set_message_id;
+ vtab->get_in_reply_to = fvf_get_in_reply_to;
+ vtab->set_in_reply_to = fvf_set_in_reply_to;
+
+ ((etch_object*)vtab)->vtab = (vtabmask*)((etch_object*)fakevf)->vtab; /* chain vtabs */
+ etch_cache_insert(((etch_object*)vtab)->hashkey, vtab, 0);
+ }
+
+ CU_ASSERT_EQUAL_FATAL(((etch_object*)vtab)->class_id, CLASS_ID);
+
+ ((etch_object*)fakevf)->vtab = (vtabmask*)vtab; /* set override vtab */
+
+ fakevf->impl = (etch_object*) new_fake_vf_impl(items); /* create vf instance data */
+
+ return fakevf;
+}
+
+
+#if(0)
+
+/* = = = = = = = = = =
+ * TDI IMPLEMENTATION
+ * = = = = = = = = = =
+ */
+
+/**
+ * fake_tdi_impl
+ * instance data for a TDI implementation
+ */
+typedef struct fake_tdi_impl
+{
+ etch_object object;
+
+ etch_type* tdi_type;
+ etch_message* xmessage;
+ etch_iterator iterator;
+ etch_value_factory* vf;
+ testitems* testdata;
+ byte started, done, ended, is_owned_message;
+
+} fake_tdi_impl;
+
+
+
+/**
+ * destroy_fake_tdi_impl
+ * memory cleanup handler for fake_tdi_impl
+ */
+static int destroy_fake_tdi_impl(void* data)
+{
+ fake_tdi_impl* impl = (fake_tdi_impl*)data;
+ etch_destructor destroy = NULL;
+ int result = verify_object((etch_object*)impl, OBJTYPE_FAKETDI_IMPL, CLASSID_FAKETDI_IMPL, 0);
+ if (result == -1) return -1; /* object passed was not expected object */
+
+ /* type is a reference, it does not belong to the tdi. message is created
+ * in the tdi, but ownership is assumed to transfer to the caller when
+ * message_read returns. if this is not the case then is_owned_message
+ * must have been set elsewhere.
+ */
+ if (impl->is_owned_message)
+ { /* not the default case, see comment above */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impl->xmessage);
+ etch_object_destroy(impl->xmessage);
+ }
+
+ etch_free(impl);
+ return 0;
+}
+
+
+/**
+ * new_fake_tdi_impl()
+ * constructor for TDI implementation instance data
+ */
+static fake_tdi_impl* new_fake_tdi_impl(etch_value_factory* vf, etch_type* static_type, testitems* testdata)
+{
+ fake_tdi_impl* data = (fake_tdi_impl*)
+ new_object(sizeof(fake_tdi_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDI_IMPL);
+
+ /* value factory and type are references, memory not owned by tdi */
+ data->tdi_type = static_type;
+ data->vf = vf;
+ data->testdata = testdata;
+
+ data->destroy = destroy_fake_tdi_impl;
+ return data;
+}
+
+
+/**
+ * faketdi_start_message() overrides tdi_start_message()
+ */
+static etch_message* faketdi_start_message(tagged_data_input* tdi)
+{
+ int result = 0;
+ fake_tdi_impl* tdidata = NULL;
+ etch_message* newmessage = NULL;
+ etch_value_factory* vf = NULL;
+ const int READONLY = TRUE, DEFSIZE = 0, DEFDELTA = 0;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+
+ tdidata = (fake_tdi_impl*) tdi->impl; /* validate instance data */
+ result = verify_object((etch_object*)tdidata, OBJTYPE_FAKETDI_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdidata->vf);
+
+ etch_object_destroy(NULL); /* ensure we can call into instance destructor */
+ CU_ASSERT_EQUAL(result,-1);
+
+ CU_ASSERT_EQUAL(tdidata->started,FALSE);
+ CU_ASSERT_EQUAL(tdidata->done,FALSE);
+ CU_ASSERT_EQUAL(tdidata->ended,FALSE);
+
+ tdidata->started = TRUE;
+
+ /* create a message (struct) to receive the elements read. a message owns all
+ * its memory except the value factory and type (note that the vf might need
+ * to be refcounted for that reason, to ensure that if the service shuts down
+ * while a message is in the pipeline, a vf is not destroyed prior to a message
+ * which references it). the type is passed through to the underlying struct.
+ * a struct owns all its memory except type, so it is assigned a reference to
+ * the tdi's type, which is an object global to the service. ownership of the
+ * struct, and by extension the messsage, memory, is assumed to be passed to
+ * the caller when this function returns. if instead it was desired that the
+ * tdi own the message and hence the struct, an indicator of such and an
+ * associated message dtor call, would need to be coded in the tdi and tdi
+ * destructor implementation.
+ */
+ newmessage = new_message (tdidata->tdi_type, 0, tdidata->vf);
+
+ tdidata->xmessage = newmessage;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdidata->xmessage);
+
+ set_iterator(&tdidata->iterator, /* establish iterator on the input data */
+ tdidata->testdata->items, &tdidata->testdata->items->iterable);
+
+ /* while an empty dataset is valid in the general case,
+ * this test assumes that input is non-empty */
+ result = tdidata->iterator.has_next(&tdidata->iterator);
+ CU_ASSERT_EQUAL(result,TRUE);
+
+ return newmessage;
+}
+
+
+/**
+ * faketdi_read_struct_element() overrides tdi_read_struct_element()
+ */
+static int faketdi_read_struct_element(tagged_data_input* tdi, etch_struct_element* out_se)
+{
+ int result = 0;
+ fake_tdi_impl* data = NULL;
+ etch_iterator* iterator = NULL;
+ etch_destructor destroy = NULL;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(out_se);
+
+ data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->done,FALSE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+ iterator = &data->iterator;
+
+ if (iterator->has_next(iterator))
+ {
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator->current_key);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator->current_value);
+
+ out_se->key = (etch_field*) iterator->current_key;
+ out_se->value = (etch_object*) iterator->current_value;
+
+ iterator->next(iterator);
+ return TRUE;
+ }
+
+ data->done = TRUE;
+ return FALSE;
+}
+
+
+/**
+ * faketdi_end_message() overrides tdi_end_struct()
+ */
+static etch_message* faketdi_end_message(tagged_data_input* tdi, etch_message* msg)
+{
+ int result = 0;
+ fake_tdi_impl* data = NULL;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+
+ data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_EQUAL_FATAL(data->xmessage, msg);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->done,TRUE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+
+ data->ended = TRUE;
+ return msg;
+}
+
+
+/**
+ * faketdi_close() ala java test
+ * also tested here is that we can call base methods on a derived object. we invoke
+ * the TDI destructor here, which is an overide of the etchobject destructor.
+ * the TDI destructor walks the vtable chain to its parent, and invokes the parent
+ * destructor to destroy etchobject content such as any exception, and finally
+ * the object itself.
+ */
+static void faketdi_close(tagged_data_input* tdi)
+{
+ etch_object_destroy(tdi);
+}
+
+
+/**
+ * new_fake_tdi()
+ * constructor for TDI implementation
+ */
+static tagged_data_input* new_fake_tdi(etch_type* static_type, etch_value_factory* vfobj, testitems* data)
+{
+ tagged_data_input* faketdi = NULL;
+ i_tagged_data_input* vtab = NULL;
+ const unsigned short CLASS_ID = CLASSID_FAKETDI;
+ CU_ASSERT_EQUAL_FATAL(is_good_type(static_type),TRUE);
+
+ faketdi = new_tagged_data_input();
+
+ vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+ if(!vtab)
+ {
+ vtab = new_vtable(((etch_object*)faketdi)->vtab, sizeof(i_tagged_data_input), CLASS_ID);
+
+ /* override three i_tagged_data_input methods */
+ vtab->start_message = faketdi_start_message;
+ vtab->end_message = faketdi_end_message;
+ vtab->read_struct_element = faketdi_read_struct_element;
+
+ ((etch_object*)vtab)->vtab = faketdi->vtab; /* chain parent vtab to override vtab */
+ cache_insert(vtab->hashkey, vtab, FALSE);
+ }
+
+ CU_ASSERT_EQUAL_FATAL(((etch_object*)vtab)->class_id, CLASS_ID);
+
+ ((etch_object*)faketdi)->vtab = vtab; /* set override vtab */
+
+ faketdi->impl = (etch_object*)
+ new_fake_tdi_impl(vfobj, static_type, data); /* create TDI instance data */
+
+ switch(g_which_exception_test)
+ { case EXCPTEST_UNCHECKED_STATICTEXT:
+ etch_throw((etch_object*) faketdi, EXCPTYPE_NULLPTR, NULL, 0);
+ break;
+ case EXCPTEST_CHECKED_COPYTEXT:
+ etch_throw((etch_object*) faketdi, EXCPTYPE_CHECKED_BOGUS, L"copied text", ETCHEXCP_COPYTEXT | ETCHEXCP_FREETEXT);
+ break;
+ case EXCPTEST_CHECKED_STATICTEXT:
+ etch_throw((etch_object*) faketdi, EXCPTYPE_CHECKED_BOGUS, local_excp_text, ETCHEXCP_STATICTEXT);
+ break;
+ }
+
+ return faketdi;
+}
+
+
+/* = = = = = = = = = =
+ * TDO IMPLEMENTATION
+ * = = = = = = = = = =
+ */
+
+/**
+ * fake_tdo_impl
+ * instance data for a TDO implementation
+ */
+typedef struct fake_tdo_impl
+{
+ etch_object object;
+
+ byte started, ended, closed;
+ etch_message* xmessage; /* reference */
+ etch_hashtable* fakeout; /* fake output sink */
+
+} fake_tdo_impl;
+
+
+/**
+ * destroy_fake_tdo_impl
+ * memory cleanup handler for fake_tdo_impl
+ * we do not destroy the message and underlying struct, this is a reference.
+ */
+static int destroy_fake_tdo_impl(void* data)
+{
+ fake_tdo_impl* impl = (fake_tdo_impl*)data;
+ etch_destructor destroy = NULL;
+ int result = verify_object((etch_object*)impl, OBJTYPE_FAKETDO_IMPL, CLASSID_FAKETDO_IMPL, NULL);
+ if (result == -1) return -1; /* object passed was not expected object */
+
+ /* destroy the fake output sink, in this case a map. we do not want the map to
+ * destroy its content because the message owns that content, and we did not
+ * in this case give the output sink copies of that content.
+ */
+ etch_object_destroy(impl->fakeout);
+
+ etch_free(impl);
+ return 0;
+}
+
+
+/**
+ * new_fake_tdo_impl()
+ * constructor for TDO implementation instance data
+ */
+static fake_tdo_impl* new_fake_tdo_impl(etch_message* msg)
+{
+ fake_tdo_impl* data = (fake_tdo_impl*)
+ new_object(sizeof(fake_tdo_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDO_IMPL);
+
+ data->xmessage = msg; /* reference, not owned */
+
+ data->destroy = destroy_fake_tdo_impl;
+ return data;
+}
+
+
+/**
+ * faketdo_start_message() overrides tdo_start_message()
+ */
+static etch_message* faketdo_start_message(tagged_data_output* tdo, etch_message* msg)
+{
+ int result = 0;
+ fake_tdo_impl* data = NULL;
+ etch_destructor destructor = NULL;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+
+ data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDO_IMPL, 0, (void**) &destructor);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ etch_object_destroy(NULL); /* ensure we can call instance destructor */
+ CU_ASSERT_EQUAL(result,-1);
+
+ CU_ASSERT_EQUAL(data->started,FALSE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+
+ CU_ASSERT_EQUAL_FATAL(msg, data->xmessage);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->fakeout);
+
+ data->started = TRUE;
+ return msg;
+}
+
+
+/**
+ * faketdo_write_struct_element() overrides tdo_write_struct_element()
+ */
+static int faketdo_write_struct_element(tagged_data_output* tdo, etch_field* key, etch_object* val)
+{
+ int result = 0;
+ fake_tdo_impl* data = NULL;
+ etch_hashtable* fakeout = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* myentry = &hashbucket;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo); /* validate parameters */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(val);
+
+ result = is_good_field(key);
+ CU_ASSERT_EQUAL_FATAL(result,TRUE);
+
+ data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ fakeout = data->fakeout;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(fakeout);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(fakeout->realtable);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->ended, FALSE);
+ CU_ASSERT_EQUAL(data->closed,FALSE);
+
+ /* do the write to the fake output */
+ result = ((etch_object*)fakeout)->vtab->insert(fakeout->realtable, key, HASHSIZE_FIELD, val, 0, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ /* verify the write by accessing the item just written */
+ result = ((etch_object*)fakeout)->vtab->find(fakeout->realtable, key, HASHSIZE_FIELD, 0, &myentry);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_EQUAL_FATAL(myentry->key, (void*)key);
+ CU_ASSERT_EQUAL_FATAL(myentry->value, val);
+
+ return 0;
+}
+
+
+/**
+ * faketdo_end_message() overrides tdo_end_message()
+ */
+static etch_message* faketdo_end_message(tagged_data_output* tdo, etch_message* msg)
+{
+ int result = 0;
+ fake_tdo_impl* data = NULL;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+
+ data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->xmessage);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+ CU_ASSERT_EQUAL(data->closed,FALSE);
+ CU_ASSERT_EQUAL(data->xmessage, msg);
+
+ data->ended = TRUE;
+ return msg;
+}
+
+
+/**
+ * faketdo_close() ala java test
+ */
+static void faketdo_close(tagged_data_output* tdo)
+{
+ etch_object_destroy(tdo);
+}
+
+
+/**
+ * new_fake_tdo()
+ * constructor for TDO implementation
+ */
+static tagged_data_output* new_fake_tdo(etch_message* msg)
+{
+ tagged_data_output* faketdo = NULL;
+ i_tagged_data_output* vtab = NULL;
+ fake_tdo_impl* impl = NULL;
+ const unsigned short CLASS_ID = CLASSID_FAKETDO;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msg);
+
+ faketdo = new_tagged_data_output();
+
+ vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+ if(!vtab)
+ {
+ vtab = new_vtable(((etch_object*)faketdo)->vtab, sizeof(i_tagged_data_output), CLASS_ID);
+
+ /* override three i_tagged_data_output methods */
+ vtab->start_message = faketdo_start_message;
+ vtab->end_message = faketdo_end_message;
+ vtab->write_struct_element = faketdo_write_struct_element;
+
+ ((etch_object*)vtab)->vtab = faketdo->vtab; /* chain parent vtab */
+
+ cache_insert(vtab->hashkey, vtab, FALSE);
+ }
+
+ CU_ASSERT_EQUAL_FATAL(((etch_object*)vtab)->class_id, CLASS_ID);
+
+ ((etch_object*)faketdo)->vtab = vtab; /* set override vtab */
+
+ impl = new_fake_tdo_impl(msg); /* construct TDO instance data */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impl);
+
+ impl->fakeout = new_hashtable(16); /* instantiate fake output target */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impl->fakeout);
+ impl->fakeout->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+
+ faketdo->impl = (etch_object*) impl;
+
+ return faketdo;
+}
+
+#endif /* #if(0) */
+
+
+/* = = = = = = = = = =
+ * TEST DATA
+ * = = = = = = = = = =
+ */
+
+/*
+ * testdata_clear_handler()
+ * memory callback on testdata clear
+ */
+static int testdata_clear_handler (void* key, void* value)
+{
+ return TRUE; /* indicate free handled */
+}
+
+/*
+ * new_testitems()
+ * instantiate and return types and fields used in tests
+ */
+static testitems* new_testitems()
+{
+ testitems* items = etch_malloc(sizeof(struct testitems), 0);
+ items->mt1 = new_type(L"mt1");
+ items->mt2 = new_type(L"mt2");
+ items->rmt = new_type(L"rmt");
+ items->mf1 = new_field(L"x");
+ items->mf2 = new_field(L"y");
+ items->mf3 = new_field(L"s1");
+ items->mf4 = new_field(L"s2");
+
+ items->s1 = new_stringw(L"string1");
+ items->s2 = new_stringw(L"string2");
+ items->n1 = new_int32(1);
+ items->n2 = new_int32(2);
+ items->l1 = new_int64(12345);
+ items->l2 = new_int64(54321);
+
+ items->mf_bool_d0_1 = new_field(L"f1");
+ items->mf_bool_d0_2 = new_field(L"f2");
+ items->mf_bool_d1_1 = new_field(L"f3");
+ items->mf_bool_d1_2 = new_field(L"f4");
+
+ items->mf_int32_d0_1 = new_field(L"f5");
+ items->mf_int32_d0_2 = new_field(L"f6");
+ items->mf_int32_d1_1 = new_field(L"f7");
+ items->mf_int32_d1_2 = new_field(L"f8");
+
+ items->mf_str_d0_1 = new_field(L"f9");
+ items->mf_str_d0_2 = new_field(L"fa");
+ items->mf_str_d1_1 = new_field(L"fb");
+ items->mf_str_d1_2 = new_field(L"fc");
+
+ items->mf_message_id = new_field(L"_messageId");
+ items->mf_in_reply_to = new_field(L"_inReplyTo");
+
+ etchtype_put_validator(items->mt1, clone_field(items->mf_message_id), (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(items->mt1, clone_field(items->mf1), (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(items->mt1, clone_field(items->mf2), (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(items->mt1, clone_field(items->mf3), (etch_object*) etchvtor_string_get(0));
+ etchtype_put_validator(items->mt1, clone_field(items->mf4), (etch_object*) etchvtor_string_get(0));
+
+ etchtype_put_validator(items->rmt, clone_field(items->mf_message_id), (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(items->rmt, clone_field(items->mf_in_reply_to),(etch_object*) etchvtor_int64_get(0));
+
+ #if(0)
+ etchtype_put_validator(items->mt1, items->mf_bool_d0_1->clone(items->mf_bool_d0_1), (etch_object*) etchvtor_boolean_get(0));
+ etchtype_put_validator(items->mt1, items->mf_bool_d0_2->clone(items->mf_bool_d0_2), (etch_object*) etchvtor_boolean_get(0));
+
+ etchtype_put_validator(items->mt1, items->mf_bool_d1_1->clone(items->mf_bool_d1_1), (etch_object*) etchvtor_boolean_get(1));
+ etchtype_put_validator(items->mt1, items->mf_bool_d1_2->clone(items->mf_bool_d1_2), (etch_object*) etchvtor_boolean_get(1));
+
+ etchtype_put_validator(items->mt1, items->mf_int32_d0_1->clone(items->mf_int32_d0_1), (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(items->mt1, items->mf_int32_d0_2->clone(items->mf_int32_d0_2), (etch_object*) etchvtor_int32_get(0));
+
+ etchtype_put_validator(items->mt1, items->mf_int32_d1_1->clone(items->mf_int32_d1_1), (etch_object*) etchvtor_int32_get(1));
+ etchtype_put_validator(items->mt1, items->mf_int32_d1_2->clone(items->mf_int32_d1_2), (etch_object*) etchvtor_int32_get(1));
+
+ etchtype_put_validator(items->mt1, items->mf_str_d0_1->clone(items->mf_str_d0_1), (etch_object*) etchvtor_string_get(0));
+ etchtype_put_validator(items->mt1, items->mf_str_d0_2->clone(items->mf_str_d0_2), (etch_object*) etchvtor_string_get(0));
+
+ etchtype_put_validator(items->mt1, items->mf_str_d1_1->clone(items->mf_str_d1_1), (etch_object*) etchvtor_string_get(1));
+ etchtype_put_validator(items->mt1, items->mf_str_d1_2->clone(items->mf_str_d1_2), (etch_object*) etchvtor_string_get(1));
+ #endif
+
+ items->items = new_hashtable(0);
+ items->items->is_readonly_keys = FALSE;
+ items->items->is_readonly_values = FALSE;
+ items->items->is_tracked_memory = TRUE;
+ items->items->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+ items->items->freehook = testdata_clear_handler;
+ return items;
+}
+
+/*
+ * destroy_testitems()
+ * free memory for test data
+ */
+static void destroy_testitems(testitems* items)
+{
+ etch_object_destroy(items->mt1);
+ etch_object_destroy(items->mt2);
+ etch_object_destroy(items->rmt);
+ etch_object_destroy(items->mf1);
+ etch_object_destroy(items->mf2);
+ etch_object_destroy(items->mf3);
+ etch_object_destroy(items->mf4);
+
+ etch_object_destroy(items->s1);
+ etch_object_destroy(items->s2);
+ etch_object_destroy(items->n1);
+ etch_object_destroy(items->n2);
+ etch_object_destroy(items->l1);
+ etch_object_destroy(items->l2);
+
+ etch_object_destroy (items->mf_message_id);
+ etch_object_destroy(items->mf_in_reply_to);
+
+ etch_object_destroy(items->mf_bool_d0_1);
+ etch_object_destroy(items->mf_bool_d0_2);
+ etch_object_destroy(items->mf_bool_d1_1);
+ etch_object_destroy(items->mf_bool_d1_2);
+
+ etch_object_destroy(items->mf_int32_d0_1);
+ etch_object_destroy(items->mf_int32_d0_2);
+ etch_object_destroy(items->mf_int32_d1_1);
+ etch_object_destroy(items->mf_int32_d1_2);
+
+ etch_object_destroy(items->mf_str_d0_1);
+ etch_object_destroy(items->mf_str_d0_2);
+ etch_object_destroy(items->mf_str_d1_1);
+ etch_object_destroy(items->mf_str_d1_2);
+
+ etchvtor_clear_cache(); /* free all cached validators */
+
+ destroy_hashtable(items->items, FALSE, FALSE);
+
+ etch_free(items);
+}
+
+
+#if 0
+/*
+ * compare_lists()
+ * compares specified message content with content of some other map.
+ * returns boolean indicating equal or not.
+ */
+static int compare_lists(etch_message* msg, etch_hashtable* otherdata)
+{
+ int thiscount = 0, fakecount = 0, result = 0;
+ etch_structvalue* svx = NULL;
+ etch_iterator iterator;
+ etch_hashitem hashbucket;
+ etch_hashitem* sventry = &hashbucket;
+
+ svx = msg->sv;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(svx);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(svx->items);
+
+ thiscount = ((struct i_hashtable*)((etch_object*)svx->items)->vtab)->count(svx->items->realtable,0,0);
+ fakecount = ((struct i_hashtable*)((etch_object*)otherdata)->vtab)->count(otherdata->realtable,0,0);
+ CU_ASSERT_EQUAL(fakecount, thiscount);
+ if (fakecount != thiscount) return FALSE;
+
+ set_iterator(&iterator, otherdata, &otherdata->iterable);
+
+ while(((struct i_iterable*)iterator.object.vtab)->has_next(&iterator) && result == 0)
+ {
+ result = ((struct i_hashtable*)((etch_object*)svx->items)->vtab)->findh
+ (svx->items->realtable, ((etch_object*)iterator.current_key)->hashkey,
+ otherdata, (void**)&sventry);
+ CU_ASSERT_EQUAL(iterator.current_value, sventry->value);
+
+ ((struct i_iterable*)iterator.object.vtab)->next(&iterator);
+ }
+
+ return TRUE;
+}
+
+#endif
+
+#if 0
+/*
+ * hashtable_clear_handler()
+ * override callback from hashtable during clear()
+ */
+static int hashtable_clear_handler (void* key, void* value)
+{
+ /* prior to calling clear() on any hashtable htab, set:
+ * htab.callback = hashtable_clear_handler;
+ * and the hashtable will call back here for each item in the table,
+ * replacing any such handler installed. return FALSE to have the hashtable
+ * proceed as it would with a handler. save off the existing handler and call
+ * it, to proceed as if you had not replaced the handler. return TRUE to
+ * indicate you handled the free and the hashtable should not attempt to free
+ * memory for key and/or value, if in fact it would have done so otherwise.
+ * note that structs always set such a callback on their backing store
+ * in order to free key and value allocations.
+ */
+ return FALSE;
+}
+
+#endif
+/* = = = = = = = = = =
+ * TESTS
+ * = = = = = = = = = =
+ */
+
+/*
+ * run_iterator_test()
+ * iterate over test collection, inserting pairs to specified message.
+ * iterate over message, veriying that content matches test collection.
+ */
+static int run_iterator_test(etch_message* msg, testitems* data)
+{
+ etch_iterator* iterator = NULL;
+ int msgcount = 0, testcount = 0, result = 0;
+
+ iterator = new_iterator(data->items, &data->items->iterable);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator);
+ CU_ASSERT_EQUAL(iterator->ordinal,1);
+
+ while(iterator->has_next(iterator))
+ {
+ testcount++;
+
+ result = message_put(msg, iterator->current_key, iterator->current_value);
+
+ CU_ASSERT_EQUAL(result,0);
+
+ iterator->next(iterator);
+ }
+
+ set_iterator(iterator, msg->sv->items, &msg->sv->items->iterable);
+ CU_ASSERT_EQUAL(iterator->ordinal,1);
+
+ while(iterator->has_next(iterator))
+ {
+ msgcount++;
+ iterator->next(iterator);
+ }
+
+ CU_ASSERT_EQUAL(testcount, msgcount);
+ etch_object_destroy(iterator);
+ return msgcount;
+}
+
+
+/*
+ * iterator_test()
+ */
+static void iterator_test(void)
+{
+ int result = 0;
+ testitems* data = NULL;
+ etch_hashtable* map = NULL;
+ etch_message* msg = NULL;
+ etch_value_factory* vf = NULL;
+
+ etch_string *sval1 = NULL, *sval2 = NULL;
+ etch_int32 *ival1 = NULL, *ival2 = NULL;
+ etch_field *skey1 = NULL, *skey2 = NULL;
+ etch_field *ikey1 = NULL, *ikey2 = NULL;
+
+ data = new_testitems();
+ map = data->items;
+
+ /* a message owns all its memory except the value factory and type, so we'll
+ * make copies of all the testdata keys and values to be inserted into it */
+ ikey1 = (etch_field*)etch_object_clone_func(data->mf1);
+ ikey2 = (etch_field*)etch_object_clone_func(data->mf2);
+ skey1 = (etch_field*)etch_object_clone_func(data->mf3);
+ skey2 = (etch_field*)etch_object_clone_func(data->mf4);
+
+ sval1 = (etch_string*)etch_object_clone_func(data->s1);
+ sval2 = (etch_string*)etch_object_clone_func(data->s2);
+ ival1 = (etch_int32*)etch_object_clone_func(data->n1);
+ ival2 = (etch_int32*)etch_object_clone_func(data->n2);
+
+ /* insert cloned fields and values to the test input collection */
+ ((struct i_hashtable*)((etch_object*)map)->vtab)->inserth(map->realtable, ikey1, ival1, map, 0);
+ ((struct i_hashtable*)((etch_object*)map)->vtab)->inserth(map->realtable, ikey2, ival2, map, 0);
+ ((struct i_hashtable*)((etch_object*)map)->vtab)->inserth(map->realtable, skey1, sval1, map, 0);
+ ((struct i_hashtable*)((etch_object*)map)->vtab)->inserth(map->realtable, skey2, sval2, map, 0);
+
+ vf = new_fake_value_factory(data);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(vf);
+ CU_ASSERT_FALSE_FATAL(is_etch_exception(vf));
+
+ /* a message owns its memory except vf and type, so we give it global type */
+ msg = new_message (data->mt1, 0, vf);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msg);
+ CU_ASSERT_FALSE_FATAL(is_etch_exception(msg));
+
+ result = run_iterator_test(msg, data);
+ CU_ASSERT_EQUAL(result,4); /* 4 items in message */
+
+ etch_object_destroy(msg);
+ etch_object_destroy(vf);
+
+ destroy_testitems(data);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+/*
+ * reply_test
+ * test message.reply
+ */
+static void reply_test(void)
+{
+ int result = 0;
+ etch_message* msg = NULL;
+ etch_message* newmsg = NULL;
+ etch_type* replytype = NULL;
+ etch_value_factory* vf = NULL;
+ etch_int64* id_original = NULL;
+ etch_int64* id_replied_to = NULL;
+ int64 val_origid = 0, val_replyid = 0;
+ testitems* data = new_testitems();
+
+ id_original = data->l1;
+
+ vf = new_fake_value_factory(data);
+
+ msg = new_message (data->mt1, 0, vf); /* message owns neither arg */
+
+ result = message_set_id(msg, (etch_int64*)etch_object_clone_func(id_original));
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ replytype = data->rmt; /* again, a message does not own a type */
+
+ newmsg = message_reply (msg, replytype);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newmsg);
+ CU_ASSERT_FALSE_FATAL(is_etch_exception(newmsg));
+
+ result = is_equal_types(data->rmt, newmsg->sv->struct_type);
+ CU_ASSERT_TRUE(result);
+ CU_ASSERT_EQUAL(vf, newmsg->vf);
+
+ id_replied_to = message_get_in_reply_to(newmsg);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(id_replied_to);
+ CU_ASSERT_FALSE_FATAL(is_etch_exception(id_replied_to));
+
+ val_origid = id_original->value;
+ val_replyid = id_replied_to->value;
+ CU_ASSERT_EQUAL(val_origid, val_replyid);
+
+ etch_object_destroy(newmsg);
+ etch_object_destroy(msg);
+ etch_object_destroy(vf);
+ destroy_testitems(data);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+#if 0
+/*
+ * run_exception_test
+ * execute an individual exception test
+ */
+static void run_exception_test(const int whichtest)
+{
+ etch_type* static_type = NULL;
+ etch_value_factory* vf = NULL;
+ testitems* data = new_testitems();
+
+ static_type = new_type(L"type1");
+ vf = new_fake_value_factory(data);
+
+ #if(0)
+ /* create a bogus TDI inheriting from tagged data input */
+ tdi = new_fake_tdi(static_type, vf, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+
+ switch(whichtest)
+ { case EXCPTEST_UNCHECKED_STATICTEXT:
+ case EXCPTEST_CHECKED_COPYTEXT:
+ case EXCPTEST_CHECKED_STATICTEXT:
+ { CU_ASSERT_TRUE_FATAL(is_exception(tdi));
+ #if IS_DEBUG_CONSOLE
+ wprintf(L"\ncaught %s exception on tdi\n", tdi->result->exception->excptext);
+ #endif
+ }
+ }
+
+ msg = message_read(NULL); /* pass NULL for TDI */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msg);
+ CU_ASSERT_TRUE(is_exception(msg));
+ excp = get_exception(msg);
+ CU_ASSERT_EQUAL(excp->excptype, EXCPTYPE_ILLEGALARG);
+ #if IS_DEBUG_CONSOLE
+ wprintf(L"\ncaught %s exception on message\n", msg->result->exception->excptext);
+ #endif
+
+ /* free TDI */
+ faketdi_close(tdi);
+
+ /* free struct, it is just a shell with no content other than the exception */
+ etch_object_destroy(msg);
+
+ #endif
+
+ etch_object_destroy(vf);
+
+ destroy_testitems(data);
+ etch_object_destroy(static_type);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+#endif
+
+
+
+/**
+ * main
+ */
+//int _tmain(int argc, _TCHAR* argv[])
+CU_pSuite test_etch_message_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("suite_message", init_suite, clean_suite);
+ CU_add_test(pSuite, "test iterator over message", iterator_test);
+ CU_add_test(pSuite, "message reply test", reply_test);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/message/test_structvalue.c b/binding-c/runtime/c/src/test/message/test_structvalue.c
new file mode 100644
index 0000000..6a1cac1
--- /dev/null
+++ b/binding-c/runtime/c/src/test/message/test_structvalue.c
@@ -0,0 +1,1045 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_structvalue.c -- test etch_structvalue object
+ */
+
+#include "etch_runtime.h"
+#include "etch_connection.h"
+#include "etch_encoding.h"
+#include "etch_log.h"
+#include "etch_structval.h"
+#include "etch_nativearray.h"
+#include "etch_objecttypes.h"
+#include "etch_exception.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * individual setup and teardown
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+static etch_type *mt1, *mt2;
+
+static etch_field *mf_bool_d0_1;
+static etch_field *mf_bool_d0_2;
+static etch_field *mf_bool_d1_1;
+static etch_field *mf_bool_d1_2;
+
+static etch_field *mf_int32_d0_1;
+static etch_field *mf_int32_d0_2;
+static etch_field *mf_int32_d1_1;
+static etch_field *mf_int32_d1_2;
+
+static etch_field *mf_str_d0_1;
+static etch_field *mf_str_d0_2;
+static etch_field *mf_str_d1_1;
+static etch_field *mf_str_d1_2;
+
+static etch_hashtable* testdata;
+
+static int g_which_exception_test;
+
+#if(0)
+#define OBJTYPE_FAKETDI_IMPL ETCHTYPEB_INSTANCEDATA
+#define CLASSID_FAKETDI_IMPL 0xf0
+#define OBJTYPE_FAKETDO_IMPL ETCHTYPEB_INSTANCEDATA
+#define CLASSID_FAKETDO_IMPL 0xf1
+#define OBJTYPE_FAKETDI_VTABLE 0x88
+#define OBJTYPE_FAKETDO_VTABLE 0x89
+#define CLASSID_FAKETDI 0xf6
+#define CLASSID_FAKETDO 0xf7
+#endif
+
+#define EXCPTEST_UNCHECKED_STATICTEXT 1
+#define EXCPTEST_CHECKED_COPYTEXT 2
+#define EXCPTEST_CHECKED_STATICTEXT 3
+
+
+#if(0)
+
+/**
+ * fake_tdi_impl
+ * instance data for a TDI implementation
+ */
+typedef struct fake_tdi_impl
+{
+ etch_object object;
+
+ etch_type* tdi_type;
+ byte started, done, ended, is_owned_struct;
+ etch_structvalue* xstruct;
+ etch_iterator iterator;
+
+} fake_tdi_impl;
+
+
+/**
+ * fake_tdo_impl
+ * instance data for a TDO implementation
+ */
+typedef struct fake_tdo_impl
+{
+ etch_object object;
+
+ byte started, ended, closed;
+ etch_structvalue* xstruct;
+ etch_hashtable* fakeout; /* fake output stream */
+
+} fake_tdo_impl;
+
+
+/**
+ * fake_tdi_impl_destroy_handler
+ * memory cleanup handler for fake_tdi_impl
+ */
+static int destroy_fake_tdi_impl(fake_tdi_impl* impl)
+{
+ etch_destructor destroy = NULL;
+ int result = verify_object((etch_object*)impl, OBJTYPE_FAKETDI_IMPL, CLASSID_FAKETDI_IMPL, NULL);
+ if (result == -1) return -1; /* object passed was not expected object */
+
+ /* type is a refrence, it does not belong to the tdi. struct is created
+ * in the tdi, but ownership is assumed to transfer to the caller when
+ * structvalue_read returns. if this is not the case then is_owned_struct
+ * must have been set elsewhere
+ */
+ if (impl->is_owned_struct)
+ { /* not the default case, see comment above */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impl->xstruct);
+ etch_object_destroy(impl->xstruct);
+ }
+
+ etch_free(impl);
+
+ return 0;
+}
+
+
+/**
+ * fake_tdo_impl_destroy_handler
+ * memory cleanup handler for fake_tdo_impl
+ */
+static int destroy_fake_tdo_impl(fake_tdo_impl* impl)
+{
+ etch_destructor destroy = NULL;
+ int result = verify_object((etch_object*)impl, OBJTYPE_FAKETDO_IMPL, CLASSID_FAKETDO_IMPL, NULL);
+ if (result == -1) return -1; /* object passed was not expected object */
+
+ /* destroy the fake output receptor, in this case a hashtable. we don't want the
+ * hashtable to destroy its content because its content is references to someone
+ * else's content, in this case the host struct value.
+ */
+ destroy_hashtable(impl->fakeout, FALSE, FALSE);
+
+ /* we do not destroy the struct value, this is merely a reference */
+ /* impl->((etch_object*)xstruct)->vtab->destroy(impl->xstruct); */
+
+ etch_free(impl);
+ return 0;
+}
+
+
+/**
+ * new_fake_tdi_impl()
+ * constructor for TDI implementation instance data
+ */
+static fake_tdi_impl* new_fake_tdi_impl(etch_type* static_type)
+{
+ fake_tdi_impl* data = (fake_tdi_impl*)
+ new_object(sizeof(fake_tdi_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDI_IMPL);
+
+ /* assign type - this is a reference, it is not our memory */
+ data->tdi_type = static_type;
+
+ /* set destructor */
+ data->destroy = destroy_fake_tdi_impl;
+ return data;
+}
+
+
+/**
+ * new_fake_tdo_impl()
+ * constructor for TDO implementation instance data
+ */
+static fake_tdo_impl* new_fake_tdo_impl(etch_structvalue* sv)
+{
+ fake_tdo_impl* data = (fake_tdo_impl*)
+ new_object(sizeof(fake_tdo_impl), ETCHTYPEB_INSTANCEDATA, CLASSID_FAKETDO_IMPL);
+
+ data->xstruct = sv;
+
+ /* set destructor */
+ data->destroy = destroy_fake_tdo_impl;
+ return data;
+}
+
+
+enum objtyp ETCHTYPE_VTABLE_FAKETDI = 0xf0;
+enum objtyp ETCHTYPE_VTABLE_FAKETDO = 0xf1;
+
+
+/**
+ * faketdi_start_struct() overrides tdi_start_struct()
+ */
+static etch_structvalue* faketdi_start_struct(tagged_data_input* tdi)
+{
+ int result = 0;
+ fake_tdi_impl* data = NULL;
+ etch_structvalue* newstructval = NULL;
+ const int READONLY = TRUE, DEFSIZE = 0, DEFDELTA = 0;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+
+ data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = etch_object_destroy(NULL); /* ensure we can call into instance data destructor */
+ CU_ASSERT_EQUAL(result,-1);
+
+ CU_ASSERT_EQUAL(data->started,FALSE);
+ CU_ASSERT_EQUAL(data->done,FALSE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+
+ data->started = TRUE;
+
+ /* create a struct to receive the elements read. a struct owns all its memory
+ * so we give it a copy of the tdi's type. ownership of the struct is assumed
+ * to be passed to the caller when this function returns. if instead it was
+ * desired that the tdi own the struct, data.is_owned_struct would have to
+ * be set subsequently elsewhere.
+ * note that sv no longer owns type, so we no longer clone the type
+ */
+ newstructval = new_structvalue (data->tdi_type, 0);
+
+ data->xstruct = newstructval;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->xstruct);
+
+ /* establish iterator on the fake input data */
+ set_iterator(&data->iterator, testdata, &testdata->iterable);
+
+ /* while an empty dataset is valid in the general case,
+ * this test assumes that input is non-empty */
+ result = data->iterator.vtab->has_next(&data->iterator);
+ CU_ASSERT_EQUAL(result,TRUE);
+
+ return data->xstruct;
+}
+
+
+/**
+ * faketdo_start_struct() overrides tdo_start_struct()
+ */
+static int faketdo_start_struct(tagged_data_output* tdo, etch_structvalue* structval)
+{
+ int result = 0;
+ fake_tdo_impl* data = NULL;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+
+ data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ result = data->destroy(NULL); /* ensure we can call instance data destructor */
+ CU_ASSERT_EQUAL(result,-1);
+
+ CU_ASSERT_EQUAL(data->started,FALSE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+
+ CU_ASSERT_EQUAL_FATAL(structval, data->xstruct);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->fakeout);
+
+ data->started = TRUE;
+ return 0;
+}
+
+
+
+/**
+ * faketdo_write_struct_element() overrides tdo_write_struct_element()
+ */
+static int faketdo_write_struct_element(tagged_data_output* tdo, etch_field* key, etch_object* val)
+{
+ int result = 0;
+ fake_tdo_impl* data = NULL;
+ etch_hashtable* fakeout = NULL;
+ etch_hashitem hashbucket;
+ etch_hashitem* myentry = &hashbucket;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo); /* validate parameters */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(val);
+
+ result = is_good_field(key);
+ CU_ASSERT_EQUAL_FATAL(result,TRUE);
+
+ data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ fakeout = data->fakeout;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(fakeout);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(fakeout->realtable);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->ended, FALSE);
+ CU_ASSERT_EQUAL(data->closed,FALSE);
+
+ /* do the write to the fake output */
+ result = ((etch_object*)fakeout)->vtab->insert(fakeout->realtable, key, HASHSIZE_FIELD, val, 0, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ /* verify the write by accessing the item just written */
+ result = ((etch_object*)fakeout)->vtab->find(fakeout->realtable, key, HASHSIZE_FIELD, 0, &myentry);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_EQUAL_FATAL(myentry->key, (void*)key);
+ CU_ASSERT_EQUAL_FATAL(myentry->value, val);
+
+ return 0;
+}
+
+
+/**
+ * faketdi_read_struct_element() overrides tdi_read_struct_element()
+ */
+static int faketdi_read_struct_element(tagged_data_input* tdi, etch_struct_element* out_se)
+{
+ int result = 0;
+ fake_tdi_impl* data = NULL;
+ etch_iterator* iterator = NULL;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(out_se);
+
+ data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->done,FALSE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+ iterator = &data->iterator;
+
+ if (iterator->has_next(iterator))
+ {
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator->current_key);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator->current_value);
+
+ out_se->key = (etch_field*) iterator->current_key;
+ out_se->value = (etch_object*) iterator->current_value;
+
+ iterator->next(iterator);
+ return TRUE;
+ }
+
+ data->done = TRUE;
+ return FALSE;
+}
+
+
+/**
+ * faketdi_end_struct() overrides tdi_end_struct()
+ */
+static int faketdi_end_struct(tagged_data_input* tdi, etch_structvalue* sv)
+{
+ int result = 0;
+ fake_tdi_impl* data = NULL;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi->impl);
+
+ data = (fake_tdi_impl*) tdi->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDI_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->xstruct);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->done,TRUE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+ CU_ASSERT_EQUAL(data->xstruct,sv);
+
+ data->ended = TRUE;
+ return 0;
+}
+
+
+/**
+ * faketdo_end_struct() overrides tdo_end_struct()
+ */
+static int faketdo_end_struct(tagged_data_output* tdo, struct etch_structvalue* sv)
+{
+ int result = 0;
+ fake_tdo_impl* data = NULL;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdo->impl);
+
+ data = (fake_tdo_impl*) tdo->impl; /* validate instance data */
+ result = verify_object((etch_object*)data, OBJTYPE_FAKETDO_IMPL, 0, 0);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(data->xstruct);
+
+ CU_ASSERT_EQUAL(data->started,TRUE);
+ CU_ASSERT_EQUAL(data->ended,FALSE);
+ CU_ASSERT_EQUAL(data->closed,FALSE);
+ CU_ASSERT_EQUAL(data->xstruct, sv);
+
+ data->ended = TRUE;
+ return 0;
+}
+
+
+/**
+ * faketdi_close() ala java test
+ * also tested here is that we can call base methods on a derived object. we invoke
+ * the TDI destructor here, which is an overide of the etchobject destructor.
+ * the TDI destructor walks the vtable chain to its parent, and invokes the parent
+ * destructor to destroy etchobject content such as any exception, and finally
+ * the object itself.
+ */
+static void faketdi_close(tagged_data_input* tdi)
+{
+ tdi->destroy(tdi);
+}
+
+
+/**
+ * faketdo_close() ala java test
+ */
+static void faketdo_close(tagged_data_output* tdo)
+{
+ tdo->destroy(tdo);
+}
+
+
+/**
+ * new_fake_tdi()
+ * constructor for TDI implementation
+ */
+static tagged_data_input* new_fake_tdi(etch_type* static_type)
+{
+ tagged_data_input* faketdi = NULL;
+ i_tagged_data_input* vtab = NULL;
+ const unsigned short CLASS_ID = CLASSID_FAKETDI;
+ CU_ASSERT_EQUAL_FATAL(is_good_type(static_type),TRUE);
+
+ faketdi = new_tagged_data_input();
+
+ vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+ if(!vtab)
+ {
+ vtab = new_vtable(((etch_object*)faketdi)->vtab, sizeof(i_tagged_data_input), CLASS_ID);
+
+ /* override three i_tagged_data_input methods */
+ vtab->start_struct = faketdi_start_struct;
+ vtab->end_struct = faketdi_end_struct;
+ vtab->read_struct_element = faketdi_read_struct_element;
+
+ ((etch_object*)vtab)->vtab = faketdi->vtab; /* chain parent vtab to override vtab */
+ cache_insert(vtab->hashkey, vtab, FALSE);
+ }
+
+ CU_ASSERT_EQUAL_FATAL(((etch_object*)vtab)->class_id, CLASS_ID);
+
+ ((etch_object*)faketdi)->vtab = vtab; /* set override vtab */
+
+ faketdi->impl = (etch_object*)
+ new_fake_tdi_impl(static_type); /* create TDI instance data */
+
+ switch(g_which_exception_test)
+ { case EXCPTEST_UNCHECKED_STATICTEXT:
+ etch_throw((etch_object*) faketdi, EXCPTYPE_NULLPTR, NULL, 0);
+ break;
+ case EXCPTEST_CHECKED_COPYTEXT:
+ etch_throw((etch_object*) faketdi, EXCPTYPE_CHECKED_BOGUS, L"copied text", ETCHEXCP_COPYTEXT | ETCHEXCP_FREETEXT);
+ break;
+ case EXCPTEST_CHECKED_STATICTEXT:
+ etch_throw((etch_object*) faketdi, EXCPTYPE_CHECKED_BOGUS, local_excp_text, ETCHEXCP_STATICTEXT);
+ break;
+ }
+
+ return faketdi;
+}
+
+
+
+/**
+ * new_fake_tdo()
+ * constructor for TDO implementation
+ */
+static tagged_data_output* new_fake_tdo(etch_structvalue* sv)
+{
+ tagged_data_output* faketdo = NULL;
+ i_tagged_data_output* vtab = NULL;
+ fake_tdo_impl* impl = NULL;
+ const unsigned short CLASS_ID = CLASSID_FAKETDO;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+
+ faketdo = new_tagged_data_output();
+
+ vtab = cache_find(get_vtable_cachehkey(CLASS_ID), 0);
+
+ if(!vtab)
+ {
+ vtab = new_vtable(((etch_object*)faketdo)->vtab, sizeof(i_tagged_data_output), CLASS_ID);
+
+ /* override three i_tagged_data_output methods */
+ vtab->start_struct = faketdo_start_struct;
+ vtab->end_struct = faketdo_end_struct;
+ vtab->write_struct_element = faketdo_write_struct_element;
+
+ ((etch_object*)vtab)->vtab = faketdo->vtab; /* chain parent vtab to override vtab */
+
+ cache_insert(vtab->hashkey, vtab, FALSE);
+ }
+
+ CU_ASSERT_EQUAL_FATAL(((etch_object*)vtab)->class_id, CLASS_ID);
+
+ ((etch_object*)faketdo)->vtab = vtab; /* set override vtab */
+
+ impl = new_fake_tdo_impl(sv); /* set TDO instance data */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impl);
+
+ impl->fakeout = new_hashtable(16);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(impl->fakeout);
+ impl->fakeout->is_readonly_keys = impl->fakeout->is_readonly_values = FALSE;
+ impl->fakeout->is_tracked_memory = TRUE;
+ impl->fakeout->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+
+ faketdo->impl = (etch_object*) impl;
+
+ return faketdo;
+}
+
+#endif /* #if(0) */
+
+
+/*
+ * load_testdata_string()
+ * load testdata map with some ETCH_STRING objects
+ */
+static int load_testdata_string()
+{
+ int i = 0, numitems = 4, result = 0;
+ etch_field* newkey = NULL;
+ etch_string* newobj = NULL;
+ wchar_t* str0 = L"now ", *str1 = L"is ", *str2 = L"the ", *str3 = L"time";
+ wchar_t* strings[4] = { str0, str1, str2, str3 };
+
+ for(; i < numitems; i++)
+ {
+ newkey = new_field(strings[i]); /* testdata map CAN free keys */
+ newobj = new_stringw(strings[i]);
+ result = ((struct i_hashtable*)((etch_object*)testdata)->vtab)->inserth(testdata->realtable, newkey, newobj, 0, 0);
+ }
+
+ return numitems;
+}
+
+
+/*
+ * load_testdata_int()
+ * load testdata array with some ETCH_INT objects
+ */
+static int load_testdata_int()
+{
+ const int numitems = 4;
+ int i = 0, result = 0;
+ etch_field* newkey = NULL;
+ etch_int32* newobj = NULL;
+ wchar_t* str0 = L"fld1", *str1 = L"fld2", *str2 = L"fld3", *str3 = L"fld4";
+ wchar_t* keys[4] = { str0, str1, str2, str3 };
+ int ints[4] = { 1, 2, 3, 4 };
+
+ for(; i < numitems; i++)
+ {
+ newkey = new_field(keys[i]);
+ newobj = new_int32(ints[i]); /* testdata table can free both keys and values */
+ result = ((struct i_hashtable*)((etch_object*)testdata)->vtab)->inserth(testdata->realtable, newkey, newobj, 0, 0);
+ }
+
+ return numitems;
+}
+
+
+/*
+ * testdata_clear_handler()
+ * memory callback on testdata clear
+ */
+static int testdata_clear_handler (void* keyData, void* valueData)
+{
+ etch_object_destroy((etch_object*)keyData);
+ etch_object_destroy((etch_object*)valueData);
+ return TRUE;
+}
+
+
+/*
+ * new_testdata()
+ * create testdata map and load it up with data objects
+ */
+static int new_testdata(const int datatype)
+{
+ int count = 0;
+ #if IS_DEBUG_CONSOLE
+ etch_iterator iterator;
+ #endif
+ g_which_exception_test = 0;
+ testdata = new_hashtable(0);
+ testdata->is_readonly_keys = FALSE;
+ testdata->is_readonly_values = FALSE;
+ testdata->is_tracked_memory = TRUE;
+ testdata->content_type = ETCHHASHTABLE_CONTENT_OBJECT;
+ testdata->freehook = testdata_clear_handler;
+
+ switch(datatype)
+ { case 1: count = load_testdata_int(); break;
+ case 2: count = load_testdata_string(); break;
+ default: return -1;
+ }
+
+ #if IS_DEBUG_CONSOLE
+ printf("\n");
+ #pragma warning (disable:4313)
+ set_iterator(&iterator, testdata, &testdata->iterable);
+
+ while(iterator.vtab->has_next(&iterator))
+ {
+ printf("testdata %08x %08x\n", iterator.current_key, iterator.current_value);
+ iterator.vtab->next(&iterator);
+ }
+ printf("\n");
+ #endif
+
+ mt1 = new_type(L"one");
+ mt2 = new_type(L"two");
+
+ mf_bool_d0_1 = new_field(L"f1");
+ mf_bool_d0_2 = new_field(L"f2");
+ mf_bool_d1_1 = new_field(L"f3");
+ mf_bool_d1_2 = new_field(L"f4");
+
+ mf_int32_d0_1 = new_field(L"f5");
+ mf_int32_d0_2 = new_field(L"f6");
+ mf_int32_d1_1 = new_field(L"f7");
+ mf_int32_d1_2 = new_field(L"f8");
+
+ mf_str_d0_1 = new_field(L"f9");
+ mf_str_d0_2 = new_field(L"fa");
+ mf_str_d1_1 = new_field(L"fb");
+ mf_str_d1_2 = new_field(L"fc");
+
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_bool_d0_1), (etch_object*) etchvtor_boolean_get(0));
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_bool_d0_2), (etch_object*) etchvtor_boolean_get(0));
+
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_bool_d1_1), (etch_object*) etchvtor_boolean_get(1));
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_bool_d1_2), (etch_object*) etchvtor_boolean_get(1));
+
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_int32_d0_1), (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_int32_d0_2), (etch_object*) etchvtor_int32_get(0));
+
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_int32_d1_1), (etch_object*) etchvtor_int32_get(1));
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_int32_d1_2), (etch_object*) etchvtor_int32_get(1));
+
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_str_d0_1), (etch_object*) etchvtor_string_get(0));
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_str_d0_2), (etch_object*) etchvtor_string_get(0));
+
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_str_d1_1), (etch_object*) etchvtor_string_get(1));
+ etchtype_put_validator(mt1, (etch_field*)etch_object_clone_func(mf_str_d1_2), (etch_object*) etchvtor_string_get(1));
+
+ return count;
+}
+
+/*
+ * destroy_testdata()
+ * destroy testdata map and content
+ */
+static void destroy_testdata()
+{
+ destroy_hashtable(testdata, TRUE, TRUE);
+
+ etch_object_destroy(mt1);
+ etch_object_destroy(mt2);
+
+ destroy_field(mf_bool_d0_1);
+ destroy_field(mf_bool_d0_2);
+ destroy_field(mf_bool_d1_1);
+ destroy_field(mf_bool_d1_2);
+ destroy_field(mf_int32_d0_1);
+ destroy_field(mf_int32_d0_2);
+ destroy_field(mf_int32_d1_1);
+ destroy_field(mf_int32_d1_2);
+ destroy_field(mf_str_d0_1);
+ destroy_field(mf_str_d0_2);
+ destroy_field(mf_str_d1_1);
+ destroy_field(mf_str_d1_2);
+
+ etchvtor_clear_cache(); /* free all cached validators */
+}
+
+#if 0
+
+/*
+ * compare_lists()
+ * compares testdata content with specified struct content.
+ * returns boolean indicating equal or not.
+ */
+static int compare_lists(etch_structvalue* svx)
+{
+ int thiscount = 0, testcount = 0, result = 0;
+ etch_iterator iterator;
+ etch_hashitem hashbucket;
+ etch_hashitem* sventry = &hashbucket;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(svx);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(svx->items);
+
+ thiscount = ((struct i_hashtable*)((etch_object*)svx->items)->vtab)->count(svx->items->realtable,0,0);
+ testcount = ((struct i_hashtable*)((etch_object*)testdata)->vtab)->count(testdata->realtable,0,0);
+ CU_ASSERT_EQUAL(testcount, thiscount);
+ if (testcount != thiscount) return FALSE;
+
+ set_iterator(&iterator, testdata, &testdata->iterable);
+
+ while(iterator.has_next(&iterator) && result == 0)
+ {
+ result = ((struct i_hashtable*)((etch_object*)svx->items)->vtab)->findh
+ (svx->items->realtable, ((etch_object*)iterator.current_key)->hashkey, testdata, (void**)&sventry);
+
+ CU_ASSERT_EQUAL(iterator.current_value, sventry->value);
+
+ iterator.next(&iterator);
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * hashtable_clear_handler()
+ * override callback from hashtable during clear()
+ */
+static int hashtable_clear_handler (void* key, void* value)
+{
+ /* prior to calling clear() on any hashtable htab, set:
+ * htab.callback = hashtable_clear_handler;
+ * and the hashtable will call back here for each item in the table,
+ * replacing any such handler installed. return FALSE to have the hashtable
+ * proceed as it would with a handler. save off the existing handler and call
+ * it, to proceed as if you had not replaced the handler. return TRUE to
+ * indicate you handled the free and the hashtable should not attempt to free
+ * memory for key and/or value, if in fact it would have done so otherwise.
+ * note that structvalue always sets such a callback on its backing store
+ * in order to free key and value allocations.
+ */
+ return FALSE;
+}
+
+/*
+ * hashtable_do_nada_handler()
+ * override callback from hashtable during clear()
+ * this callback does nothing and returns TRUE, so no content memory is freed.
+ */
+static int hashtable_do_nada_handler (void* key, void* value)
+{
+ return TRUE;
+}
+#endif
+
+/*
+ * run_iterator_test
+ * test struct value iterator
+ */
+static void run_iterator_test(void)
+{
+ int result = 0, count = 0;
+ etch_iterator iterator;
+ etch_int32* int_obj_1 = NULL;
+ etch_int32* int_obj_2 = NULL;
+ etch_nativearray* int_array_1 = NULL;
+ etch_nativearray* int_array_2 = NULL;
+ etch_structvalue* sv = NULL;
+ etch_field* fldkey = NULL;
+
+ new_testdata(1); /* instantiate types and fields we use here */
+
+ /* memory for field keys and object values is given to the struct to manage.
+ * once a key and value are "put" to the struct, we give up responsibility
+ * for freeing them. therefore when we put an object to the struct, we clone
+ * a type to use as the key. note that when we assign the struct a type,
+ * however, we do not relinquish ownership of the memory for that etch_type.
+ * this is because types are global to the service in the binding,
+ * and are freed only at service shutdown.
+ */
+
+ int_obj_1 = new_int32(123);
+ int_obj_2 = new_int32(234);
+
+ int_array_1 = new_etch_nativearray(CLASSID_ARRAY_INT32, sizeof(int), 1, 3, 0, 0);
+ int_array_2 = new_etch_nativearray(CLASSID_ARRAY_INT32, sizeof(int), 1, 5, 0, 0);
+
+ sv = new_structvalue(mt1, 0);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+ result = is_etch_exception(sv);
+ CU_ASSERT_EQUAL(result, FALSE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv->items);
+
+ /* if structvalue has ETCH_STRUCTVAL_VALIDATE_ON_PUT compiled,
+ * these structvalue.put()s will be validated. */
+
+ fldkey = clone_field(mf_int32_d0_1);
+ result = structvalue_put(sv, fldkey, (etch_object*) int_obj_1);
+ CU_ASSERT_EQUAL(result, 0);
+
+ set_iterator(&iterator, sv->items, &sv->items->iterable);
+ CU_ASSERT_EQUAL(iterator.has_next(&iterator), TRUE);
+
+ fldkey = clone_field(mf_int32_d0_2);
+ result = structvalue_put(sv, fldkey, (etch_object*) int_obj_2);
+ CU_ASSERT_EQUAL(result, 0);
+
+ set_iterator(&iterator, sv->items, &sv->items->iterable);
+ CU_ASSERT_TRUE(iterator.has_next(&iterator));
+
+ iterator.next(&iterator);
+ CU_ASSERT_TRUE(iterator.has_next(&iterator));
+
+ iterator.next(&iterator);
+ CU_ASSERT_FALSE(iterator.has_next(&iterator));
+
+ fldkey = clone_field(mf_int32_d1_1);
+ result = structvalue_put(sv, fldkey, (etch_object*) int_array_1);
+ CU_ASSERT_EQUAL(result, 0);
+
+ fldkey = clone_field(mf_int32_d1_2);
+ result = structvalue_put(sv, fldkey, (etch_object*) int_array_2);
+ CU_ASSERT_EQUAL(result, 0);
+
+ set_iterator(&iterator, sv->items, &sv->items->iterable);
+
+ while(iterator.has_next(&iterator))
+ { count++;
+ iterator.next(&iterator);
+ }
+ CU_ASSERT_EQUAL(count,4);
+
+
+ /* free memory for struct and its instance data and content */
+ etch_object_destroy(sv);
+ destroy_testdata();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * run_exception_test
+ *
+ */
+static void run_exception_test(const int whichtest)
+{
+ etch_type* static_type = NULL;
+ static_type = new_type(L"type1");
+
+ #if(0)
+
+ /* create a bogus TDI inheriting from tagged data input */
+ tdi = new_fake_tdi(static_type);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tdi);
+
+ switch(whichtest)
+ { case EXCPTEST_UNCHECKED_STATICTEXT:
+ case EXCPTEST_CHECKED_COPYTEXT:
+ case EXCPTEST_CHECKED_STATICTEXT:
+ { CU_ASSERT_TRUE_FATAL(is_exception(tdi));
+ #if IS_DEBUG_CONSOLE
+ wprintf(L"\ncaught %s exception on tdi\n", tdiobj->result->exception->excptext);
+ #endif
+ }
+ }
+
+ sv = structvalue_read(NULL); /* pass NULL for TDI */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(sv);
+ CU_ASSERT_TRUE(is_exception(tdi));
+ excp = get_exception(sv);
+ CU_ASSERT_EQUAL(excp->excptype, EXCPTYPE_ILLEGALARG);
+ #if IS_DEBUG_CONSOLE
+ wprintf(L"\ncaught %s exception on sv\n", svobj->result->exception->excptext);
+ #endif
+
+ /* free TDI */
+ faketdi_close(tdi);
+
+ /* free struct, it is just a shell with no content other than the exception */
+ etch_object_destroy(sv);
+
+ #endif
+
+ /* destroy the type allocated above.
+ * in most tests we would NOT do this since the struct would own it after we
+ * created the struct. however we passed a null tdi parameter above to the
+ * method which would have created the struct. the type which would have
+ * been assigned to the struct was in the tdi that we did not pass. The tdi
+ * will not destroy its type. however note that in the real world this
+ * situation would not occur. all types would be global, and we will always
+ * create a copy of the type for the struct. see faketdi_start_struct() for
+ * an example, note that it clones the type for the new struct.
+ */
+ etch_object_destroy(static_type);
+
+ /* destroy testdata list and content */
+ destroy_testdata();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_iterator_over_hashtable
+ */
+static void test_iterator_over_hashtable(void)
+{
+ etch_iterator* iterator = NULL;
+ int testcount = 0, thiscount = 0;
+ struct i_iterable* vtab = NULL;
+ new_testdata(1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(testdata);
+ testcount = ((struct i_hashtable*)((etch_object*)testdata)->vtab)->count(testdata->realtable, 0, 0);
+ CU_ASSERT_NOT_EQUAL(testcount, 0);
+
+ iterator = new_iterator(testdata, &testdata->iterable);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator);
+ CU_ASSERT_EQUAL_FATAL(iterator->ordinal,1);
+ thiscount = 1;
+ vtab = (i_iterable*)((etch_object*)iterator)->vtab;
+ while(vtab->has_next(iterator))
+ thiscount += (vtab->next(iterator) == 0);
+
+ CU_ASSERT_EQUAL(testcount, thiscount);
+
+ destroy_iterator(iterator);
+ destroy_testdata();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+#if 0
+/*
+ * exception_test_1
+ */
+static void exception_test_1(void)
+{
+ int itemcount = 0;
+ if ((itemcount = new_testdata(1)) > 0)
+ run_exception_test(EXCPTEST_UNCHECKED_STATICTEXT);
+}
+#endif
+
+/*
+ * exception_test_2
+ */
+static void exception_test_2(void)
+{
+ int itemcount = 0;
+ if ((itemcount = new_testdata(1)) > 0)
+ run_exception_test(EXCPTEST_CHECKED_COPYTEXT);
+}
+
+#if 0
+
+/*
+ * exception_test_3
+ */
+static void exception_test_3(void)
+{
+ int itemcount = 0;
+ if ((itemcount = new_testdata(1)) > 0)
+ run_exception_test(EXCPTEST_CHECKED_STATICTEXT);
+}
+
+#endif
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_structvalue_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("suite_structvalue", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test iterator over hashtable", test_iterator_over_hashtable);
+ CU_add_test(pSuite, "test structvalue iterator", run_iterator_test);
+ CU_add_test(pSuite, "test sv exceptions", exception_test_2);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/message/test_type.c b/binding-c/runtime/c/src/test/message/test_type.c
new file mode 100644
index 0000000..c52b020
--- /dev/null
+++ b/binding-c/runtime/c/src/test/message/test_type.c
@@ -0,0 +1,698 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_type.c
+ * test etch_type
+ */
+
+#include "etch_runtime.h"
+#include "etch_type.h"
+#include "etch_validator.h"
+#include "etch_field.h"
+#include "etch_objecttypes.h"
+#include "etch_serializer.h"
+#include "etch_arraylist.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+#include "Basic.h"
+#include "Automated.h"
+#include <wchar.h>
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ //this_teardown();
+ etch_runtime_shutdown();
+ return 0;
+}
+
+int this_setup()
+{
+ return 0;
+}
+
+/*
+ * test_type
+ */
+static void test_type(void)
+{
+ int result = 0;
+ etch_type *type1 = NULL, *type2 = NULL;
+
+ const wchar_t* nametext1 = L"abracadabra";
+
+ const wchar_t* nametext2 = L"gilgamesh";
+
+#ifdef ETCH_DEBUGALLOC
+ alloc_a = etch_showmem(0, FALSE);
+#endif
+ type1 = new_type(NULL);
+ CU_ASSERT_PTR_NULL(type1);
+
+#ifdef ETCH_DEBUGALLOC
+ alloc_b = etch_showmem(0, FALSE);
+ CU_ASSERT_EQUAL(alloc_a, alloc_b);
+#endif
+
+ type1 = new_type(nametext1);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(type1);
+ CU_ASSERT_TRUE(is_good_type(type1));
+
+ type2 = new_type(nametext1);
+ CU_ASSERT_TRUE(is_equal_types(type1, type2));
+ result = memcmp(type1->name, type2->name, type1->namebytelen);
+ CU_ASSERT_EQUAL(result,0);
+
+ etch_object_destroy(type1);
+ etch_object_destroy(type2);
+
+#ifdef ETCH_DEBUGALLOC
+ alloc_a = etch_showmem(0, FALSE);
+ CU_ASSERT_EQUAL(alloc_a, alloc_b);
+#endif
+
+ type1 = new_type(nametext1);
+ type2 = new_type(nametext2);
+
+ CU_ASSERT_FALSE(is_equal_types(type1, type2));
+
+ etch_object_destroy(type1);
+ etch_object_destroy(type2);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_type_hashfunc
+ * test wire hashing function
+ */
+static void test_type_hashfunc(void)
+{
+ unsigned hash1 = 0, hash2 = 0;
+ etch_type *type1 = NULL, *type2 = NULL;
+
+ const wchar_t* nametext1 = L"abracadabra";
+ const size_t numelts1 = wcslen(nametext1);
+ const size_t numbytes1 = sizeof(wchar_t) * ( numelts1 + 1 );
+
+ const wchar_t* nametext2 = L"gilgamesh";
+ const size_t numelts2 = wcslen(nametext2);
+ const size_t numbytes2 = sizeof(wchar_t) * ( numelts2 + 1 );
+
+ hash1 = compute_type_id_from_widename(nametext1);
+ hash2 = compute_type_id_from_widename(nametext2);
+
+ CU_ASSERT_NOT_EQUAL(hash1, hash2);
+
+ type1 = new_type(nametext1);
+ type2 = new_type(nametext2);
+ CU_ASSERT_EQUAL(type1->namebytelen, numbytes1);
+ CU_ASSERT_EQUAL(type2->namebytelen, numbytes2);
+ CU_ASSERT_EQUAL(type1->id, hash1);
+ CU_ASSERT_EQUAL(type2->id, hash2);
+
+ etch_object_destroy(type1);
+ etch_object_destroy(type2);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * function pointers used by test_getset()
+ */
+static int bogus_stubhelper (void* stub, void* delsvc, void* obj, void* whofrom, void* msg) { return 0; }
+
+
+
+/**
+ * test_getset
+ * test mutators
+ */
+static void test_getset(void)
+{
+ unsigned int this_class = 0, old_class = 0;
+ unsigned char cur_flag = 0, old_flag = 0;
+ unsigned int cur_timeout = 0, old_timeout = 0;
+ opaque_stubhelper cur_helper = NULL, old_helper = NULL;
+ etch_serializer *cur_impexphelper, *old_impexphelper;
+ etch_type *type1 = new_type(L"abracadabra");
+ etch_type *type2 = new_type(L"gilgamesh"), *cur_type = NULL, *old_type = NULL;
+ etch_field *fld1 = new_field(L"field1"), *cur_field = NULL, *old_field = NULL;
+ etch_serializer* impxhelp1 = (etch_serializer*) new_object(sizeof(etch_object), ETCHTYPEB_ETCHOBJECT, CLASSID_NONE);
+ const unsigned int TEST_CLASS = (ETCHTYPEB_PRIMITIVE << 16) | CLASSID_PRIMITIVE_INT32;
+
+ /* component type */
+ this_class = etchtype_get_component_type(type1);
+ CU_ASSERT_EQUAL(this_class, 0);
+
+ old_class = etchtype_set_component_type(type1, TEST_CLASS);
+ CU_ASSERT_EQUAL(old_class, 0);
+
+ this_class = etchtype_get_component_type(type1);
+ CU_ASSERT_EQUAL(this_class, TEST_CLASS);
+
+ old_class = etchtype_set_component_type(type1, 0);
+ CU_ASSERT_EQUAL(old_class, TEST_CLASS);
+
+ /* timeout */
+ cur_timeout = etchtype_get_timeout(type1);
+ CU_ASSERT_EQUAL(cur_timeout, 0);
+
+ old_timeout = etchtype_set_timeout(type1, 1000);
+ CU_ASSERT_EQUAL(old_timeout, 0);
+
+ cur_timeout = etchtype_get_timeout(type1);
+ CU_ASSERT_EQUAL(cur_timeout, 1000);
+
+ old_timeout = etchtype_set_timeout(type1, 0);
+ CU_ASSERT_EQUAL(old_timeout, 1000);
+
+ /* result type */
+ cur_type = etchtype_get_result_type(type1);
+ CU_ASSERT_PTR_EQUAL(cur_type, NULL);
+
+ old_type = etchtype_set_result_type(type1, type2);
+ CU_ASSERT_PTR_EQUAL(old_type, NULL);
+
+ cur_type = etchtype_get_result_type(type1);
+ CU_ASSERT_PTR_EQUAL(cur_type, type2);
+
+ old_type = etchtype_set_result_type(type1, NULL);
+ CU_ASSERT_PTR_EQUAL(old_type, type2);
+
+ /* super type */
+ cur_type = etchtype_get_super_type(type1);
+ CU_ASSERT_PTR_EQUAL(cur_type, NULL);
+
+ old_type = etchtype_set_super_type(type1, type2);
+ CU_ASSERT_PTR_EQUAL(old_type, NULL);
+
+ cur_type = etchtype_get_super_type(type1);
+ CU_ASSERT_PTR_EQUAL(cur_type, type2);
+
+ old_type = etchtype_set_super_type(type1, NULL);
+ CU_ASSERT_PTR_EQUAL(old_type, type2);
+
+ /* response field */
+ cur_field = etchtype_get_response_field(type1);
+ CU_ASSERT_PTR_EQUAL(cur_field, NULL);
+
+ old_field = etchtype_set_response_field(type1, type2);
+ CU_ASSERT_PTR_EQUAL(old_field, NULL);
+
+ cur_field = etchtype_get_response_field(type1);
+ CU_ASSERT_PTR_EQUAL(cur_field, type2);
+
+ old_field = etchtype_set_response_field(type1, NULL);
+ CU_ASSERT_PTR_EQUAL(old_field, type2);
+
+ /* run validators */
+ cur_flag = etchtype_get_run_validators(type1);
+ CU_ASSERT_EQUAL(cur_flag, FALSE);
+
+ old_flag = etchtype_set_run_validators(type1, TRUE);
+ CU_ASSERT_EQUAL(old_flag, FALSE);
+
+ cur_flag = etchtype_get_run_validators(type1);
+ CU_ASSERT_EQUAL(cur_flag, TRUE);
+
+ old_flag = etchtype_set_run_validators(type1,FALSE);
+ CU_ASSERT_EQUAL(old_flag, TRUE);
+
+ /* stub helper */
+ cur_helper = etchtype_get_type_stubhelper(type1);
+ CU_ASSERT_PTR_EQUAL(cur_helper, NULL);
+
+ old_helper = etchtype_set_type_stubhelper(type1, bogus_stubhelper);
+ CU_ASSERT_PTR_EQUAL(old_helper, NULL);
+
+ old_helper = etchtype_get_type_stubhelper(type1);
+ CU_ASSERT_PTR_EQUAL(old_helper, bogus_stubhelper);
+
+ old_helper = etchtype_set_type_stubhelper(type1, NULL);
+ CU_ASSERT_PTR_EQUAL(old_helper, bogus_stubhelper);
+
+ /* import/export helper */
+ cur_impexphelper = etchtype_get_impexphelper(type1);
+ CU_ASSERT_PTR_EQUAL(cur_impexphelper, NULL);
+
+ old_impexphelper = etchtype_set_impexphelper(type1, impxhelp1);
+ CU_ASSERT_PTR_EQUAL(old_impexphelper, NULL);
+
+ cur_impexphelper = etchtype_get_impexphelper(type1);
+ CU_ASSERT_PTR_EQUAL(cur_impexphelper, impxhelp1);
+
+ old_impexphelper = etchtype_set_impexphelper(type1, NULL);
+ CU_ASSERT_PTR_EQUAL(old_impexphelper, impxhelp1);
+
+
+ etch_object_destroy(fld1);
+ etch_object_destroy(type2);
+ etch_object_destroy(type1);
+ etch_object_destroy(impxhelp1);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_assignable()
+ * test is_assignable_from()
+ */
+static void test_assignable(void)
+{
+ int result = 0;
+ const unsigned short TEST_SUPERCLASS_ID = 0xffff;
+ etch_type* type1 = new_type(L"type1");
+ etch_type* type2 = new_type(L"type2");
+ ((etch_object*)type1)->class_id = TEST_SUPERCLASS_ID;
+ etchtype_set_super_type(type2, type1);
+
+ result = etchtype_is_assignable_from(type1, type2);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ result = etchtype_is_assignable_from(type2, type1);
+ CU_ASSERT_EQUAL(result, FALSE);
+
+ result = etchtype_is_assignable_from(type2, NULL);
+ CU_ASSERT_EQUAL(result, FALSE);
+
+ result = etchtype_is_assignable_from(NULL, type1);
+ CU_ASSERT_EQUAL(result, FALSE);
+
+ etch_object_destroy(type1);
+ etch_object_destroy(type2);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_validators_access
+ * test put and get validators
+ */
+static void test_validators_access(void)
+{
+ etch_iterator iterator;
+ etch_field* vtor1_key = new_field(L"vtor1");
+ etch_field* vtor2_key = new_field(L"vtor2");
+ etch_field* bogus_key = new_field(L"bogus");
+ etch_type *type1 = new_type(L"abracadabra");
+ etch_type *typev = new_type(L"struct"); /* struct validator type */
+ etch_validator* vtor1 = etchvtor_int32_get(0);
+ etch_validator* vtor2 = etchvtor_struct_get(typev, 0);
+ etch_validator* vtor_return = NULL;
+
+ int result = etchtype_put_validator(type1, (etch_field*)etch_object_clone_func(vtor1_key), (etch_object*) vtor1);
+ CU_ASSERT_EQUAL(result, 0);
+
+ vtor_return = (etch_validator*) etchtype_get_validator_by_name(type1, vtor1_key->name);
+ CU_ASSERT_PTR_NOT_NULL(vtor_return);
+ CU_ASSERT_PTR_EQUAL(vtor_return, vtor1);
+
+ vtor_return = (etch_validator*) etchtype_get_validator_by_id(type1, vtor1_key->id);
+ CU_ASSERT_PTR_NOT_NULL(vtor_return);
+ CU_ASSERT_PTR_EQUAL(vtor_return, vtor1);
+
+ result = etchtype_put_validator(type1, (etch_field*)etch_object_clone_func(vtor2_key), (etch_object*) vtor2);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = etchtype_validators_count(type1);
+ CU_ASSERT_EQUAL(result, 2);
+
+ vtor_return = (etch_validator*) etchtype_get_validator_by_name(type1, vtor1_key->name);
+ CU_ASSERT_PTR_NOT_NULL(vtor_return);
+ CU_ASSERT_PTR_EQUAL(vtor_return, vtor1);
+
+ vtor_return = (etch_validator*) etchtype_get_validator_by_id(type1, vtor1_key->id);
+ CU_ASSERT_PTR_NOT_NULL(vtor_return);
+ CU_ASSERT_PTR_EQUAL(vtor_return, vtor1);
+
+ vtor_return = (etch_validator*) etchtype_get_validator_by_name(type1, vtor2_key->name);
+ CU_ASSERT_PTR_NOT_NULL(vtor_return);
+ CU_ASSERT_PTR_EQUAL(vtor_return, vtor2);
+
+ vtor_return = (etch_validator*) etchtype_get_validator_by_id(type1, vtor2_key->id);
+ CU_ASSERT_PTR_NOT_NULL(vtor_return);
+ CU_ASSERT_PTR_EQUAL(vtor_return, vtor2);
+
+ /* iterate over the validators map */
+ result = etchtype_set_validators_iterator(type1, &iterator);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ while(iterator.has_next(&iterator))
+ {
+ const etch_validator* this_vtor = (etch_validator*) iterator.current_value;
+
+ if (this_vtor == vtor1)
+ result = TRUE;
+ else
+ if (this_vtor == vtor2)
+ result = TRUE;
+ else result = FALSE;
+
+ CU_ASSERT_EQUAL_FATAL(result, TRUE);
+
+ iterator.next(&iterator);
+ }
+
+ /* etchtype_clear_validators() is superfluous here, destruction of the type
+ * has the same result, however we may as well test it */
+ result = etchtype_clear_validators(type1);
+ CU_ASSERT_EQUAL(result, 2); /* result of clear is cleared count */
+
+ result = etchtype_validators_count(type1);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* etchtype_put_validator() was given clones of these fields as validator
+ * keys, which were freed during etchtype_clear_validators. this is the model
+ * to use in the binding, i.e. clone a static etch_field for put_validator().
+ */
+ etch_object_destroy(vtor1_key);
+ etch_object_destroy(vtor2_key);
+ etch_object_destroy(bogus_key);
+
+ /* vtor1 will not have been freed during etchtype_clear_validators(),
+ * since it is a cached validator. likewise, calling its destructor
+ * would have no effect, etchtype_clear_validators() has already tried.
+ * etchvtor_clear_cache() will free it and any other cached validators.
+ * vtor2 will however have been freed during etchtype_clear_validators(),
+ * since a struct validator is not one of the cached validator types.
+ * in any case, we don't need to be concerned here about which validators
+ * are cached and which are not, since etchtype_clear_validators() will
+ * free the uncached ones, and etchvtor_clear_cache() the cached ones.
+ */
+ etch_object_destroy(typev);
+ etch_object_destroy(type1);
+ etchvtor_clear_cache(); /* destroy cached validators (vtor1 in this case) */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_fields_access
+ */
+static void test_fields_access(void)
+{
+ etch_iterator iterator;
+ etch_field* field1 = new_field(L"field1");
+ etch_field* field2 = new_field(L"field2");
+ etch_field* field3 = new_field(L"field3");
+ etch_field* fieldx = new_field(L"fieldx");
+ etch_field* field_return = NULL;
+ etch_field* field_clone = NULL;
+ etch_arraylist* list_return = NULL;
+ etch_type *type1 = new_type(L"abracadabra");
+ const int EXPECTED_FIELDS_COUNT = 3;
+ int result = 0, newfield_id = 0;
+
+ field_clone = (etch_field*)etch_object_clone_func(field1);
+ field_return = etchtype_add_field(type1, field_clone);
+ CU_ASSERT_PTR_NOT_NULL(field_return);
+ CU_ASSERT_PTR_EQUAL(field_return, field_clone);
+
+ /* ensure that trying to add a duplicate field returns the existing field.
+ * note that the new clone is destroyed by etchtype_add_field when it
+ * determines that the supplied field can't be added */
+ field_return = etchtype_add_field(type1, (etch_field*)etch_object_clone_func(field1));
+ CU_ASSERT_PTR_NOT_NULL(field_return);
+ CU_ASSERT_PTR_EQUAL(field_return, field_clone);
+
+ /* add a couple more fields */
+ field_clone = (etch_field*)etch_object_clone_func(field2);
+ field_return = etchtype_add_field(type1, field_clone);
+ CU_ASSERT_PTR_NOT_NULL(field_return);
+ CU_ASSERT_PTR_EQUAL(field_return, field_clone);
+
+ field_clone = (etch_field*)etch_object_clone_func(field3);
+ field_return = etchtype_add_field(type1, field_clone);
+ CU_ASSERT_PTR_NOT_NULL(field_return);
+ CU_ASSERT_PTR_EQUAL(field_return, field_clone);
+
+ result = etchtype_fields_count(type1);
+ CU_ASSERT_EQUAL(result, EXPECTED_FIELDS_COUNT);
+
+ /* test accessors */
+ field_return = etchtype_get_field_by_name(type1, field1->name);
+ CU_ASSERT_PTR_NOT_NULL(field_return);
+ CU_ASSERT_EQUAL(is_equal_fields(field_return, field1), TRUE);
+
+ field_return = etchtype_get_field_by_id(type1, field1->id);
+ CU_ASSERT_PTR_NOT_NULL(field_return);
+ CU_ASSERT_EQUAL(is_equal_fields(field_return, field1), TRUE);
+
+ field_return = etchtype_get_field_by_name(type1, field2->name);
+ CU_ASSERT_PTR_NOT_NULL(field_return);
+ CU_ASSERT_EQUAL(is_equal_fields(field_return, field2), TRUE);
+
+ field_return = etchtype_get_field_by_id(type1, field2->id);
+ CU_ASSERT_PTR_NOT_NULL(field_return);
+ CU_ASSERT_EQUAL(is_equal_fields(field_return, field2), TRUE);
+
+ field_return = etchtype_get_field_by_name(type1, field3->name);
+ CU_ASSERT_PTR_NOT_NULL(field_return);
+ CU_ASSERT_EQUAL(is_equal_fields(field_return, field3), TRUE);
+
+ field_return = etchtype_get_field_by_id(type1, field3->id);
+ CU_ASSERT_PTR_NOT_NULL(field_return);
+ CU_ASSERT_EQUAL(is_equal_fields(field_return, field3), TRUE);
+
+ /* test that a "get" of an unrecognized field name, adds a new field
+ * with that name. this is consistent with the java binding */
+ field_return = etchtype_get_field_by_name(type1, fieldx->name);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(field_return);
+ result = etchtype_fields_count(type1);
+ CU_ASSERT_EQUAL(result, EXPECTED_FIELDS_COUNT+1);
+ newfield_id = field_return->id;
+
+ /* iterate over the type's fieldmap */
+ result = etchtype_set_fields_iterator(type1, &iterator);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ while(iterator.has_next(&iterator))
+ {
+ const unsigned this_id = ((etch_field*)iterator.current_key)->id;
+
+ if (this_id == field1->id)
+ result = TRUE;
+ else
+ if (this_id == field2->id)
+ result = TRUE;
+ else
+ if (this_id == field3->id)
+ result = TRUE;
+ else
+ if (this_id == newfield_id)
+ result = TRUE;
+ else result = FALSE;
+
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ iterator.next(&iterator);
+ }
+
+ /* get a list of all the type's fields.
+ * we own the returned list, which does not own its content */
+ list_return = (etch_arraylist*) etchtype_get_fields(type1);
+
+ CU_ASSERT_EQUAL_FATAL(is_etch_arraylist(list_return), TRUE);
+ CU_ASSERT_EQUAL(list_return->count, EXPECTED_FIELDS_COUNT+1);
+
+ /* iterate over the list of fields */
+ set_iterator(&iterator, list_return, &list_return->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ const unsigned this_id = ((etch_field*)iterator.current_value)->id;
+
+ if (this_id == field1->id)
+ result = TRUE;
+ else
+ if (this_id == field2->id)
+ result = TRUE;
+ else
+ if (this_id == field3->id)
+ result = TRUE;
+ else
+ if (this_id == newfield_id)
+ result = TRUE;
+ else result = FALSE;
+
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ iterator.next(&iterator);
+ }
+
+ etch_object_destroy(list_return);
+ etch_object_destroy(field1);
+ etch_object_destroy(field2);
+ etch_object_destroy(field3);
+ etch_object_destroy(fieldx);
+ etch_object_destroy(type1);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_validators_chained
+ * test memory mangement of chained validators
+ */
+static void test_validators_chained(void)
+{
+ etch_field* vtor1_key = new_field(L"vtor1");
+ etch_field* vtor2_key = new_field(L"vtor2");
+ etch_type *structtype = new_type(L"struct"); /* struct validator type */
+ etch_validator* vtor1 = etchvtor_int32_get(0);
+ etch_validator* vtor2 = etchvtor_int16_get(0);
+ etch_validator* vtor3 = etchvtor_byte_get(0);
+ etch_validator* vtor4 = etchvtor_int32_get(0);
+ etch_validator* vtor5 = etchvtor_struct_get(structtype, 0);
+ etch_validator* vtor6 = etchvtor_struct_get(structtype, 0);
+ etch_validator* vtor7 = etchvtor_struct_get(structtype, 0);
+ etch_validator* vtor8 = etchvtor_string_get(0);
+ etch_type *type1 = new_type(L"abracadabra");
+
+ /* test 3 validators with the same key. when a validator is put to a type
+ * and the type already has one or more validators with the specified key,
+ * the new validator is chained to the existing validator */
+ int result = etchtype_put_validator(type1, (etch_field*)etch_object_clone_func(vtor1_key), (etch_object*) vtor1);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = etchtype_put_validator(type1, (etch_field*)etch_object_clone_func(vtor1_key), (etch_object*) vtor2);
+
+ result = etchtype_put_validator(type1, (etch_field*)etch_object_clone_func(vtor1_key), (etch_object*) vtor3);
+
+ /* etchtype_clear_validators is needed here, since we want to begin
+ * a new test with the type emptied of validators.
+ */
+ etchtype_clear_validators(type1);
+
+ /* test 3 validators with the same key, and 2 validators with another key.
+ * note that the c binding convention is to always get a "fresh" validator
+ * when we add a validator to a type, even though the get will return the
+ * same object when the validator type we get is cached. */
+
+ result = etchtype_put_validator(type1, (etch_field*)etch_object_clone_func(vtor1_key), (etch_object*) vtor4);
+ result = etchtype_put_validator(type1, (etch_field*)etch_object_clone_func(vtor1_key), (etch_object*) vtor5);
+ result = etchtype_put_validator(type1, (etch_field*)etch_object_clone_func(vtor1_key), (etch_object*) vtor6);
+
+ result = etchtype_put_validator(type1, (etch_field*)etch_object_clone_func(vtor2_key), (etch_object*) vtor7);
+ result = etchtype_put_validator(type1, (etch_field*)etch_object_clone_func(vtor2_key), (etch_object*) vtor8);
+
+ /* etchtype_clear_validators is superfluous here, so we test not doing it.
+ * when we destroy type1 below, the destructor for the type's instance data
+ * object destroys the type's hashtables, which causes the hashtables to
+ * first clear their content, as etchtype_clear_validators() would do.
+ */
+ /* etchtype_clear_validators(type1); */
+
+ /* etchtype_put_validator() was given clones of these fields as validator
+ * keys, which were freed during etchtype_clear_validators. this is the model
+ * to use in the binding, i.e. clone a static etch_field for put_validator().
+ */
+ etch_object_destroy(vtor1_key);
+ etch_object_destroy(vtor2_key);
+
+ etch_object_destroy(structtype);
+
+ /* the destroy() of type1 clears the type's fields and validators maps,
+ * which causes the fields, validator field keys, and non-cached validators,
+ * to be destroyed.
+ */
+ etch_object_destroy(type1);
+ etchvtor_clear_cache(); /* destroy any singleton cached validators */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_type_suite()
+{
+ CU_pSuite ps = CU_add_suite("type test suite", init_suite, clean_suite);
+
+ CU_add_test(ps, "test etch_type constructors/destructors", test_type);
+ CU_add_test(ps, "test etch_type hashing", test_type_hashfunc);
+ CU_add_test(ps, "test getters/setters", test_getset);
+ CU_add_test(ps, "test assignability", test_assignable);
+ CU_add_test(ps, "test validators access", test_validators_access);
+ CU_add_test(ps, "test fields access", test_fields_access);
+ CU_add_test(ps, "test chained validators", test_validators_chained);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/message/test_validator.c b/binding-c/runtime/c/src/test/message/test_validator.c
new file mode 100644
index 0000000..cc20df1
--- /dev/null
+++ b/binding-c/runtime/c/src/test/message/test_validator.c
@@ -0,0 +1,1515 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_validator.c
+ * test etch_validator
+ */
+#include "etch_runtime.h"
+#include "etch_validator.h"
+#include "etch_tagged_data.h"
+#include "etch_nativearray.h"
+#include "etch_structval.h"
+#include "etch_objecttypes.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+#include "Basic.h"
+#include "Automated.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+static etch_type* testtype;
+static etch_boolean* objbool_false;
+static etch_boolean* objbool_true;
+static etch_byte* objbyte_one;
+static etch_int16* objshort_two;
+static etch_int32* objint_three;
+static etch_int64* objlong_four;
+static etch_float* objfloat_five;
+static etch_double* objdouble_six;
+static etch_string* objstring_abc;
+static etch_object* objobject_null;
+static etch_nativearray* arraydim1;
+static etch_nativearray* arraydim2;
+static etch_nativearray* arraydim3;
+
+
+static void create_testobjects()
+{
+ testtype = new_type(L"abc");
+ objbool_false = new_boolean(FALSE);
+ objbool_true = new_boolean(TRUE);
+ objbyte_one = new_byte(1);
+ objshort_two = new_int16(2);
+ objint_three = new_int32(3);
+ objlong_four = new_int64(4);
+ objfloat_five = new_float(5.0);
+ objdouble_six = new_double(6.0);
+ objstring_abc = new_stringw(L"abc");
+ objobject_null = (etch_object*) new_primitive(sizeof(etch_object), CLASSID_NONE);
+ arraydim1 = new_etch_nativearray(CLASSID_ARRAY_BOOL, 1, 1, 4, 0, 0);
+ arraydim2 = new_etch_nativearray(CLASSID_ARRAY_BOOL, 1, 2, 4, 3, 0);
+ arraydim3 = new_etch_nativearray(CLASSID_ARRAY_BOOL, 1, 3, 4, 3, 2);
+}
+
+
+static void destroy_testobjects()
+{
+ etch_object_destroy(testtype); testtype = NULL;
+ etch_object_destroy(objbool_false); objbool_false = NULL;
+ etch_object_destroy(objbool_true); objbool_true = NULL;
+ etch_object_destroy(objbyte_one); objbyte_one = NULL;
+ etch_object_destroy(objshort_two); objshort_two = NULL;
+ etch_object_destroy(objint_three); objint_three = NULL;
+ etch_object_destroy(objlong_four); objlong_four = NULL;
+ etch_object_destroy(objfloat_five); objfloat_five = NULL;
+ etch_object_destroy(objdouble_six); objdouble_six = NULL;
+ etch_object_destroy(objstring_abc); objstring_abc = NULL;
+ etch_object_destroy(objobject_null);objobject_null= NULL;
+ etch_object_destroy(arraydim1); arraydim1 = NULL;
+ etch_object_destroy(arraydim2); arraydim2 = NULL;
+ etch_object_destroy(arraydim3); arraydim3 = NULL;
+}
+
+
+/**
+ * do_boolean_test()
+ * common test template for the boolean validator tests
+ */
+static void do_boolean_test(int dims, char* name, byte in_typecode,
+ unsigned short validated_class_id, etch_object* objgood, etch_object* objbad)
+{
+ int result = 0;
+ byte out_typecode = 0;
+ etch_validator* vtor = etchvtor_boolean_get(dims);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(vtor);
+ CU_ASSERT_EQUAL(vtor->numdimensions, dims);
+ CU_ASSERT_EQUAL(validated_class_id, vtor->expected_class_id);
+ CU_ASSERT_STRING_EQUAL(name, vtor->description);
+
+ result = vtor->validate(vtor, objgood);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = vtor->validate(vtor, objbad);
+ CU_ASSERT_EQUAL(result,-1);
+ if (result != -1)
+ result = 1;
+
+ result = vtor->check_value(vtor, objgood, &out_typecode);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(in_typecode, out_typecode);
+
+ result = vtor->check_value(vtor, objbad, &out_typecode);
+ CU_ASSERT_EQUAL(result, -1);
+
+ CU_ASSERT_EQUAL(vtor->is_cached, TRUE);
+ etch_object_destroy(vtor); /* test that vtor is cached */
+ CU_ASSERT_EQUAL(result, -1); /* and cannot be destroyed */
+}
+
+
+/**
+ * test_boolean_validator()
+ */
+static void test_boolean_validator(void)
+{
+ char* strbool0 = "bool[0]";
+ create_testobjects();
+
+ do_boolean_test(0, "bool[0]", ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE,
+ CLASSID_PRIMITIVE_BOOL, (etch_object*) objbool_false, (etch_object*) objbyte_one);
+
+ do_boolean_test(0, "bool[0]", ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE,
+ CLASSID_PRIMITIVE_BOOL, (etch_object*) objbool_true, (etch_object*) objshort_two);
+
+ do_boolean_test(0, "bool[0]", ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE,
+ CLASSID_PRIMITIVE_BOOL, (etch_object*) objbool_true, (etch_object*) objint_three);
+
+ do_boolean_test(0, strbool0, ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE,
+ CLASSID_PRIMITIVE_BOOL, (etch_object*) objbool_true, (etch_object*) objlong_four);
+
+ do_boolean_test(0, strbool0, ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE,
+ CLASSID_PRIMITIVE_BOOL, (etch_object*) objbool_true, (etch_object*) objfloat_five);
+
+ do_boolean_test(0, strbool0, ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE,
+ CLASSID_PRIMITIVE_BOOL, (etch_object*) objbool_true, (etch_object*) objdouble_six);
+
+ do_boolean_test(0, strbool0, ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE,
+ CLASSID_PRIMITIVE_BOOL, (etch_object*) objbool_true, (etch_object*) objstring_abc);
+
+ do_boolean_test(0, strbool0, ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE,
+ CLASSID_PRIMITIVE_BOOL, (etch_object*) objbool_true, objobject_null);
+
+ do_boolean_test(1, "bool[1]", ETCH_XTRNL_TYPECODE_ARRAY,
+ CLASSID_ARRAY_BOOL, (etch_object*) arraydim1, (etch_object*) objbool_true);
+
+ do_boolean_test(2, "bool[2]", ETCH_XTRNL_TYPECODE_ARRAY,
+ CLASSID_ARRAY_BOOL, (etch_object*) arraydim2, (etch_object*) arraydim1);
+
+ do_boolean_test(3, "bool[3]", ETCH_XTRNL_TYPECODE_ARRAY,
+ CLASSID_ARRAY_BOOL, (etch_object*) arraydim3, (etch_object*) arraydim2);
+
+ destroy_testobjects();
+ etchvtor_clear_cache(); /* destroy cached validators */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * do_recursive_test()
+ * run a validate on an anonymous object. if the object is a nativearray,
+ * this method is invoked recursively until the array elements are validated.
+ * the caller relinquishes ownership of valobj to this method.
+ */
+static void do_recursive_test(etch_validator* vtor, const int dims, byte* typecodes, etch_object* valobj)
+{
+ int result = 0;
+ signed char expected_typecode = 0, validated_typecode = 0;
+ //++debug_count;
+ //if (debug_count == 0x28)
+ // result = 0; // debugger target
+
+ result = vtor->validate(vtor, (etch_object*) valobj);
+ CU_ASSERT_EQUAL(result, 0);
+
+ expected_typecode = typecodes[dims];
+ result = vtor->check_value(vtor, (etch_object*) valobj, (unsigned char*)&validated_typecode);
+ CU_ASSERT_EQUAL(result,0);
+ if (expected_typecode == validated_typecode)
+ result = TRUE;
+ else result = FALSE; // debugger target
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ if (dims)
+ { /* if validating an array, iterate the array and recursively validate
+ * each array element, which could itself be an array. todo -- this
+ * is coded for nativearray, we may need to generalize to arrayvalue
+ */
+ int itemcount = 0, i = 0;
+ etch_nativearray* a = NULL;
+ etch_validator* element_vtor = vtor->element_validator(vtor);
+ CU_ASSERT_EQUAL_FATAL(is_etch_nativearray(valobj), TRUE);
+
+ a = (etch_nativearray*) valobj;
+ itemcount = (int) a->dimension[dims-1];
+
+ for(; i < (const int) itemcount; i++)
+ {
+ etch_object* subobj = etch_nativearray_get_element(a, i);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(subobj);
+
+ do_recursive_test(element_vtor, dims-1, typecodes, subobj);
+
+ }
+
+ etch_object_destroy(valobj);
+ }
+
+}
+
+
+/*
+ * test_array_boolean()
+ * test validator on arrays of boolean using the recursive test
+ */
+static void test_array_boolean(void)
+{
+ const int numdimensions = 2, dim0count = 3, dim1count = 2;
+ boolean x1[2][3] = { { FALSE,FALSE,FALSE,}, { FALSE,FALSE,FALSE,}, };
+ boolean x2[2][3] = { { TRUE, TRUE, TRUE, }, { TRUE, TRUE, TRUE, }, };
+ byte typecodz[3] = { ETCH_XTRNL_TYPECODE_BOOLEAN_FALSE,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+ etch_nativearray* a1 = NULL, *a2 = NULL;
+
+ /* test validate boolean array x1/a1 */
+ etch_validator* vtor = etchvtor_boolean_get(numdimensions);
+
+
+ a1 = new_etch_nativearray_from(&x1, CLASSID_ARRAY_BOOL,
+ sizeof(boolean), numdimensions, dim0count, dim1count, 0);
+ a1->is_content_owned = FALSE;
+ /* fyi we relinquish ownership of array a1 here */
+ do_recursive_test(vtor, numdimensions, typecodz, (etch_object*) a1);
+
+ /* test validate boolean array x2/a2 */
+ typecodz[0] = ETCH_XTRNL_TYPECODE_BOOLEAN_TRUE;
+ vtor = etchvtor_boolean_get(numdimensions);
+
+ a2 = new_etch_nativearray_from(&x2, CLASSID_ARRAY_BOOL,
+ sizeof(boolean), numdimensions, dim0count, dim1count, 0);
+
+ a2->is_content_owned = FALSE;
+ /* fyi we relinquish ownership of array a2 here */
+ do_recursive_test(vtor, numdimensions, typecodz, (etch_object*) a2);
+
+ /* done */
+ etchvtor_clear_cache(); /* destroy cached validator */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_array_byte()
+ * test validator on arrays of byte using the recursive test
+ */
+static void test_array_byte(void)
+{
+ const int numdimensions = 2, dim0count = 3, dim1count = 2;
+ byte x1[2][3] = { { 'a', 'b', 'c', }, { 'd', 'e', 'f',}, };
+ byte x2[2][3] = { { ETCHTYPE_MIN_BYTE,0xff,0},{ ETCHTYPE_MAX_BYTE,0xff,0, }, };
+ byte typecodz[3] = { ETCH_XTRNL_TYPECODE_BYTE,
+ ETCH_XTRNL_TYPECODE_BYTES, ETCH_XTRNL_TYPECODE_ARRAY };
+ etch_nativearray* a1 = NULL, *a2 = NULL;
+
+ /* test validate byte array x1/a1 */
+ etch_validator* vtor = etchvtor_byte_get(numdimensions);
+
+ a1 = new_etch_nativearray_from(&x1, CLASSID_ARRAY_BYTE,
+ sizeof(byte), numdimensions, dim0count, dim1count, 0);
+
+ /* fyi we relinquish ownership of array a1 here */
+ do_recursive_test(vtor, numdimensions, typecodz, (etch_object*) a1);
+
+ /* test validate byte array x2/a2 */
+ vtor = etchvtor_byte_get(numdimensions);
+
+ a2 = new_etch_nativearray_from(&x2, CLASSID_ARRAY_BYTE,
+ sizeof(byte), numdimensions, dim0count, dim1count, 0);
+
+ /* fyi we relinquish ownership of array a2 here */
+ do_recursive_test(vtor, numdimensions, typecodz, (etch_object*) a2);
+
+ /* done */
+ etchvtor_clear_cache(); /* destroy cached validator */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_array_int16()
+ * test validator on arrays of short using the recursive test
+ */
+static void test_array_int16(void)
+{
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+ etch_nativearray* a = NULL;
+
+ short x1[2][3][4] =
+ { { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ };
+ short x2[2][3][4] =
+ { { { 1,1,1,1, }, { 1,1,1,1, }, { 1,1,1,1, }, },
+ { { 1,1,1,1, }, { 1,1,1,1, }, { 1,1,1,1, }, },
+ };
+ short x3[2][3][4] =
+ { { { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, },
+ { { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, },
+ };
+ short x4[2][3][4] =
+ { { { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ },
+ { { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ },
+ };
+ short x5[2][3][4] =
+ { { { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ },
+ { { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ },
+ };
+ short x6[2][3][4] =
+ { { { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ },
+ { { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ },
+ };
+ short x7[2][3][4] =
+ { { { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ },
+ { { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ },
+ };
+
+ byte typecodz1[4] = { ETCH_XTRNL_TYPECODE_BYTE,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ byte typecodz2[4] = { ETCH_XTRNL_TYPECODE_SHORT,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ etch_validator* vtor = etchvtor_int16_get(numdimensions); /* vtor for all tests */
+
+ /* test validate short array x1/tc1 (1-byte shorts) */
+ a = new_etch_nativearray_from(&x1, CLASSID_ARRAY_INT16,
+ sizeof(short), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate short array x2/tc1 (1-byte shorts) */
+ a = new_etch_nativearray_from(&x2, CLASSID_ARRAY_INT16,
+ sizeof(short), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate short array x3/tc1 (1-byte shorts) */
+ a = new_etch_nativearray_from(&x3, CLASSID_ARRAY_INT16,
+ sizeof(short), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate short array x4/tc1 (1-byte shorts) */
+ a = new_etch_nativearray_from(&x4, CLASSID_ARRAY_INT16,
+ sizeof(short), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate short array x5/tc1 (1-byte shorts) */
+ a = new_etch_nativearray_from(&x5, CLASSID_ARRAY_INT16,
+ sizeof(short), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate short array x6/tc2 (2-byte shorts) */
+ a = new_etch_nativearray_from(&x6, CLASSID_ARRAY_INT16,
+ sizeof(short), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz2, (etch_object*) a);
+
+
+ /* test validate short array x7/tc2 (2-byte shorts) */
+ a = new_etch_nativearray_from(&x7, CLASSID_ARRAY_INT16,
+ sizeof(short), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz2, (etch_object*) a);
+
+
+ /* done */
+ etchvtor_clear_cache(); /* destroy cached validator */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_array_int32()
+ * test validator on arrays of int using the recursive test
+ */
+static void test_array_int32(void)
+{
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+ etch_nativearray* a = NULL;
+
+ int x1[2][3][4] =
+ { { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ };
+ int x2[2][3][4] =
+ { { { 1,1,1,1, }, { 1,1,1,1, }, { 1,1,1,1, }, },
+ { { 1,1,1,1, }, { 1,1,1,1, }, { 1,1,1,1, }, },
+ };
+ int x3[2][3][4] =
+ { { { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, },
+ { { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, },
+ };
+ int x4[2][3][4] =
+ { { { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ },
+ { { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ },
+ };
+ int x5[2][3][4] =
+ { { { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ },
+ { { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ },
+ };
+ int x6[2][3][4] =
+ { { { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ },
+ { { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ },
+ };
+ int x7[2][3][4] =
+ { { { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ },
+ { { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ },
+ };
+
+ byte typecodz1[4] = { ETCH_XTRNL_TYPECODE_BYTE,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ byte typecodz2[4] = { ETCH_XTRNL_TYPECODE_SHORT,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+
+ /* test validate int array x1/tc1 (1-byte ints) */
+ etch_validator* vtor = etchvtor_int32_get(numdimensions); /* allocates object */
+
+ a = new_etch_nativearray_from(&x1, CLASSID_ARRAY_INT32,
+ sizeof(int), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate int array x2/tc1 (1-byte ints) */
+ vtor = etchvtor_int32_get(numdimensions); /* gets cached object */
+
+ a = new_etch_nativearray_from(&x2, CLASSID_ARRAY_INT32,
+ sizeof(int), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate int array x3/tc1 (1-byte ints) */
+ a = new_etch_nativearray_from(&x3, CLASSID_ARRAY_INT32,
+ sizeof(int), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate int array x4/tc1 (1-byte ints) */
+ a = new_etch_nativearray_from(&x4, CLASSID_ARRAY_INT32,
+ sizeof(int), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate int array x5/tc1 (1-byte ints) */
+ a = new_etch_nativearray_from(&x5, CLASSID_ARRAY_INT32,
+ sizeof(int), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate int array x6/tc2 (2-byte ints) */
+ a = new_etch_nativearray_from(&x6, CLASSID_ARRAY_INT32,
+ sizeof(int), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz2, (etch_object*) a);
+
+
+ /* test validate int array x7/tc2 (2-byte ints) */
+ a = new_etch_nativearray_from(&x7, CLASSID_ARRAY_INT32,
+ sizeof(int), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz2, (etch_object*) a);
+
+
+ /* test validate int array x8/tc4 (4-byte ints) */
+ a = new_etch_nativearray_from(&x6, CLASSID_ARRAY_INT32,
+ sizeof(int), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz2, (etch_object*) a);
+
+
+ /* test validate int array x9/tc4 (4-byte ints) */
+ a = new_etch_nativearray_from(&x7, CLASSID_ARRAY_INT32,
+ sizeof(int), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz2, (etch_object*) a);
+
+
+ /* done */
+ etchvtor_clear_cache(); /* destroy cached validator */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_array_int64()
+ * test validator on arrays of long long using the recursive test
+ */
+static void test_array_int64(void)
+{
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+ etch_nativearray* a = NULL;
+
+ int64 x1[2][3][4] =
+ { { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ };
+ int64 x2[2][3][4] =
+ { { { 1,1,1,1, }, { 1,1,1,1, }, { 1,1,1,1, }, },
+ { { 1,1,1,1, }, { 1,1,1,1, }, { 1,1,1,1, }, },
+ };
+ int64 x3[2][3][4] =
+ { { { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, },
+ { { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, },
+ };
+ int64 x4[2][3][4] =
+ { { { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ },
+ { { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ { ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE,ETCHTYPE_MAX_BYTE, },
+ },
+ };
+ int64 x5[2][3][4] =
+ { { { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ },
+ { { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ { ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE,ETCHTYPE_MIN_BYTE, },
+ },
+ };
+ int64 x6[2][3][4] =
+ { { { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ },
+ { { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ { ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16,ETCHTYPE_MAX_INT16, },
+ },
+ };
+ int64 x7[2][3][4] =
+ { { { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ },
+ { { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ { ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16,ETCHTYPE_MIN_INT16, },
+ },
+ };
+ int64 x8[2][3][4] =
+ { { { ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32, },
+ { ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32, },
+ { ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32, },
+ },
+ { { ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32, },
+ { ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32, },
+ { ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32,ETCHTYPE_MAX_INT32, },
+ },
+ };
+ int64 x9[2][3][4] =
+ { { { ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32, },
+ { ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32, },
+ { ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32, },
+ },
+ { { ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32, },
+ { ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32, },
+ { ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32,ETCHTYPE_MIN_INT32, },
+ },
+ };
+ int64 x10[2][3][4] =
+ { { { ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64, },
+ { ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64, },
+ { ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64, },
+ },
+ { { ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64, },
+ { ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64, },
+ { ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64,ETCHTYPE_MAX_INT64, },
+ },
+ };
+ int64 x11[2][3][4] =
+ { { { ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64, },
+ { ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64, },
+ { ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64, },
+ },
+ { { ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64, },
+ { ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64, },
+ { ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64,ETCHTYPE_MIN_INT64, },
+ },
+ };
+
+ byte typecodz1[4] = { ETCH_XTRNL_TYPECODE_BYTE,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ byte typecodz2[4] = { ETCH_XTRNL_TYPECODE_SHORT,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ byte typecodz4[4] = { ETCH_XTRNL_TYPECODE_INT,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ byte typecodz8[4] = { ETCH_XTRNL_TYPECODE_LONG,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ /* test validate int array x1/tc1 (1-byte ints) */
+ etch_validator* vtor = etchvtor_int64_get(numdimensions); /* allocates object */
+
+ a = new_etch_nativearray_from(&x1, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate int64 array x2/tc1 (1-byte longs) */
+ vtor = etchvtor_int64_get(numdimensions); /* gets cached object */
+
+ a = new_etch_nativearray_from(&x2, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate int64 array x3/tc1 (1-byte longs) */
+ a = new_etch_nativearray_from(&x3, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate int64 array x4/tc1 (1-byte longs) */
+ a = new_etch_nativearray_from(&x4, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate int64 array x5/tc1 (1-byte longs) */
+ a = new_etch_nativearray_from(&x5, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate int64 array x6/tc2 (2-byte longs) */
+ a = new_etch_nativearray_from(&x6, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz2, (etch_object*) a);
+
+
+ /* test validate int64 array x7/tc2 (2-byte longs) */
+ a = new_etch_nativearray_from(&x7, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz2, (etch_object*) a);
+
+
+ /* test validate int64 array x8/tc4 (4-byte longs) */
+ a = new_etch_nativearray_from(&x8, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz4, (etch_object*) a);
+
+
+ /* test validate int64 array x9/tc4 (4-byte longs) */
+ a = new_etch_nativearray_from(&x9, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz4, (etch_object*) a);
+
+
+ /* test validate int64 array x10/tc8 (8-byte longs) */
+ a = new_etch_nativearray_from(&x10, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz8, (etch_object*) a);
+
+
+ /* test validate int64 array x11/tc8 (8-byte longs) */
+ a = new_etch_nativearray_from(&x11, CLASSID_ARRAY_INT64,
+ sizeof(int64), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz8, (etch_object*) a);
+
+
+ /* done */
+ etchvtor_clear_cache(); /* destroy cached validator */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_array_float()
+ * test validator on arrays of float using the recursive test
+ */
+static void test_array_float(void)
+{
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+ etch_nativearray* a = NULL;
+
+ float x1[2][3][4] =
+ { { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ };
+ float x2[2][3][4] =
+ { { { 1,1,1,1, }, { 1,1,1,1, }, { 1,1,1,1, }, },
+ { { 1,1,1,1, }, { 1,1,1,1, }, { 1,1,1,1, }, },
+ };
+ float x3[2][3][4] =
+ { { { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, },
+ { { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, },
+ };
+ float x4[2][3][4] =
+ { { { ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT, },
+ { ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT, },
+ { ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT, },
+ },
+ { { ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT, },
+ { ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT, },
+ { ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT,ETCHTYPE_MAX_FLOAT, },
+ },
+ };
+ float x5[2][3][4] =
+ { { { ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT, },
+ { ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT, },
+ { ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT, },
+ },
+ { { ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT, },
+ { ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT, },
+ { ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT,ETCHTYPE_MIN_FLOAT, },
+ },
+ };
+
+ byte typecodz1[4] = { ETCH_XTRNL_TYPECODE_FLOAT,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ etch_validator* vtor = etchvtor_float_get(numdimensions); /* vtor for all tests */
+
+ /* test validate float array x1/tc1 (1-byte floats) */
+ a = new_etch_nativearray_from(&x1, CLASSID_ARRAY_FLOAT,
+ sizeof(float), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate float array x2/tc1 (1-byte floats) */
+ a = new_etch_nativearray_from(&x2, CLASSID_ARRAY_FLOAT,
+ sizeof(float), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate float array x3/tc1 (1-byte floats) */
+ a = new_etch_nativearray_from(&x3, CLASSID_ARRAY_FLOAT,
+ sizeof(float), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate float array x4/tc1 (1-byte floats) */
+ a = new_etch_nativearray_from(&x4, CLASSID_ARRAY_FLOAT,
+ sizeof(float), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate float array x5/tc1 (1-byte floats) */
+ a = new_etch_nativearray_from(&x5, CLASSID_ARRAY_FLOAT,
+ sizeof(float), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* done */
+ etchvtor_clear_cache(); /* destroy cached validator */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_array_double()
+ * test validator on arrays of double using the recursive test
+ */
+static void test_array_double(void)
+{
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+ etch_nativearray* a = NULL;
+
+ double x1[2][3][4] =
+ { { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ };
+ double x2[2][3][4] =
+ { { { 1,1,1,1, }, { 1,1,1,1, }, { 1,1,1,1, }, },
+ { { 1,1,1,1, }, { 1,1,1,1, }, { 1,1,1,1, }, },
+ };
+ double x3[2][3][4] =
+ { { { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, },
+ { { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, { -1,-1,-1,-1, }, },
+ };
+ double x4[2][3][4] =
+ { { { ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE, },
+ { ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE, },
+ { ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE, },
+ },
+ { { ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE, },
+ { ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE, },
+ { ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE,ETCHTYPE_MAX_DOUBLE, },
+ },
+ };
+ double x5[2][3][4] =
+ { { { ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE, },
+ { ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE, },
+ { ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE, },
+ },
+ { { ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE, },
+ { ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE, },
+ { ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE,ETCHTYPE_MIN_DOUBLE, },
+ },
+ };
+
+ byte typecodz1[4] = { ETCH_XTRNL_TYPECODE_DOUBLE,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ etch_validator* vtor = etchvtor_double_get(numdimensions); /* vtor for all tests */
+
+ /* test validate double array x1/tc1 (1-byte doubles) */
+ a = new_etch_nativearray_from(&x1, CLASSID_ARRAY_DOUBLE,
+ sizeof(double), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate double array x2/tc1 (1-byte doubles) */
+ a = new_etch_nativearray_from(&x2, CLASSID_ARRAY_DOUBLE,
+ sizeof(double), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate double array x3/tc1 (1-byte doubles) */
+ a = new_etch_nativearray_from(&x3, CLASSID_ARRAY_DOUBLE,
+ sizeof(double), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate double array x4/tc1 (1-byte doubles) */
+ a = new_etch_nativearray_from(&x4, CLASSID_ARRAY_DOUBLE,
+ sizeof(double), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate double array x5/tc1 (1-byte doubles) */
+ a = new_etch_nativearray_from(&x5, CLASSID_ARRAY_DOUBLE,
+ sizeof(double), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* done */
+ etchvtor_clear_cache(); /* destroy cached validator */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_array_string()
+ * test validator on arrays of etch_string* using the recursive test
+ */
+static void test_array_string(void)
+{
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+ etch_nativearray* a = NULL;
+ int i = 0, j = 0, k = 0;
+ const wchar_t* emptystring = L"";
+ const wchar_t* nonemptystring = L"abc";
+
+ etch_string* x1[2][3][4] =
+ { { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ };
+
+ etch_string* x2[2][3][4] =
+ { { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ };
+
+ byte typecodz1[4] = { ETCH_XTRNL_TYPECODE_EMPTY_STRING,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ byte typecodz2[4] = { ETCH_XTRNL_TYPECODE_STRING,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY };
+
+ etch_validator* vtor = etchvtor_string_get(numdimensions); /* vtor for all tests */
+
+ for(i=0; i < dim2count; i++) /* populate test data arrays */
+ for(j=0; j < dim1count; j++)
+ for(k=0; k < dim0count; k++)
+ {
+ x1[i][j][k] = new_stringw(emptystring);
+ x2[i][j][k] = new_stringw(nonemptystring);
+ }
+
+ /* test validate etch_string* array x1/tc1 (empty string) */
+ a = new_etch_nativearray_from(&x1, CLASSID_ARRAY_STRING,
+ sizeof(etch_string*), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz1, (etch_object*) a);
+
+
+ /* test validate etch_string* array x2/tc2 (nonempty string) */
+ a = new_etch_nativearray_from(&x2, CLASSID_ARRAY_STRING,
+ sizeof(etch_string*), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz2, (etch_object*) a);
+
+
+ /* done */
+ etchvtor_clear_cache(); /* destroy cached validator */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_array_object()
+ * test validator on arrays of etch_object* using the recursive test
+ */
+static void test_array_object(void)
+{
+ const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2;
+ etch_nativearray* a = NULL;
+ etch_object* obj = NULL;
+ int i = 0, j = 0, k = 0;
+ int sequence = 0;
+
+ etch_object* x1[2][3][4] =
+ { { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ };
+
+ etch_object* x2[2][3][4] =
+ { { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ { { 0,0,0,0, }, { 0,0,0,0, }, { 0,0,0,0, }, },
+ };
+
+ byte typecodz[4] = { ETCH_XTRNL_TYPECODE_ANY,
+ ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY, ETCH_XTRNL_TYPECODE_ARRAY};
+
+ etch_validator* vtor = etchvtor_object_get(numdimensions); /* vtor for all tests */
+
+ for(i=0; i < dim2count; i++) /* populate test data arrays */
+ for(j=0; j < dim1count; j++)
+ for(k=0; k < dim0count; k++)
+ {
+ sequence = i*j*k;
+ obj = (etch_object*)new_int32(sequence);
+ x1[i][j][k] = obj;
+
+ obj = (etch_object*)&sequence;
+ x2[i][j][k] = obj;
+ }
+
+ /* test validate etch_object* array x1/tc (object owning object content) */
+ a = new_etch_nativearray_from(&x1, CLASSID_ARRAY_OBJECT,
+ sizeof(etch_object*), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz, (etch_object*) a);
+
+
+ /* test validate etch_object* array x2/tc (object not owning its content) */
+ a = new_etch_nativearray_from(&x2, CLASSID_ARRAY_OBJECT,
+ sizeof(etch_object*), numdimensions, dim0count, dim1count, dim2count);
+
+ do_recursive_test(vtor, numdimensions, typecodz, (etch_object*) a);
+
+
+ /* done */
+ etchvtor_clear_cache(); /* destroy cached validator */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+
+/**
+ * do_struct_element_vtor_test()
+ * common test template for the structvalue element_validator tests
+ */
+static void do_struct_element_vtor_test(int testdims, char* name, unsigned short expected_class_id, etch_type* thistype)
+{
+ etch_validator* vtor = etchvtor_struct_get(thistype, testdims);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(vtor);
+ CU_ASSERT_EQUAL(vtor->numdimensions, testdims);
+ CU_ASSERT_EQUAL(vtor->expected_class_id, expected_class_id);
+ CU_ASSERT_STRING_EQUAL(vtor->description, name);
+ CU_ASSERT_EQUAL(is_equal_types(vtor->struct_type, thistype), TRUE);
+
+ /* validator destructor is benign if validator is marked cached
+ * however a struct validator is not cached */
+ etch_object_destroy(vtor);
+}
+
+
+/**
+ * test_struct_element_validator()
+ */
+static void test_struct_element_validator(void)
+{
+ etch_type* typeabc = new_type(L"abc");
+
+ do_struct_element_vtor_test(0, "struct_abc[0]", CLASSID_STRUCTVALUE, typeabc);
+ do_struct_element_vtor_test(1, "struct_abc[1]", CLASSID_ARRAY_OBJECT, typeabc);
+ do_struct_element_vtor_test(2, "struct_abc[2]", CLASSID_ARRAY_OBJECT, typeabc);
+ do_struct_element_vtor_test(3, "struct_abc[3]", CLASSID_ARRAY_OBJECT, typeabc);
+
+ etch_object_destroy(typeabc);
+ etchvtor_clear_cache(); /* not needed since struct validator not cached */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * do_struct_goodvalue_test()
+ */
+static void do_struct_goodvalue_test(int testdims, const byte expected_typecode,
+ etch_object* value, etch_type* thistype, const int is_expected_newobject)
+{
+ int result = 0;
+ byte validated_typecode = 0;
+ etch_object* validated_value = NULL;
+
+ etch_validator* vtor = etchvtor_struct_get(thistype, testdims);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(vtor);
+
+ result = vtor->check_value(vtor, value, &validated_typecode);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ CU_ASSERT_EQUAL(validated_typecode, expected_typecode);
+
+ result = vtor->validate(vtor, value);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+
+ validated_value = vtor->validate_value(vtor, value);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(validated_value);
+
+ if (is_expected_newobject)
+ {
+ CU_ASSERT_PTR_NOT_EQUAL_FATAL(value, validated_value);
+ }
+ else CU_ASSERT_PTR_EQUAL_FATAL(value, validated_value);
+
+ /* validated_value can return the same object as paassed, or a different
+ * object. for example when we expect etch_byte but validate against an
+ * etch_int32, we would pass an etch_int32 and get back an etch_byte.
+ * in the real world, we would replace the passed value with the validated
+ * value; however in this test the caller is not prepared to handle this
+ * situation, so we free the new object, and let caller free the original */
+ if (validated_value != value)
+ etch_object_destroy(validated_value);
+
+ /* validator destructor is benign if validator is marked cached
+ * however a struct validator is not cached */
+ etch_object_destroy(vtor);
+}
+
+
+
+/**
+ * do_struct_badvalue_test()
+ */
+static void do_struct_badvalue_test(int testdims, etch_object* value, etch_type* thistype)
+{
+ int result = 0;
+ byte validated_typecode = 0;
+ etch_object* validated_value = NULL;
+
+ etch_validator* vtor = etchvtor_struct_get(thistype, testdims);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(vtor);
+
+ result = vtor->check_value(vtor, value, &validated_typecode);
+ CU_ASSERT_EQUAL_FATAL(result,-1);
+
+ result = vtor->validate(vtor, value);
+ CU_ASSERT_EQUAL_FATAL(result,-1);
+
+ validated_value = vtor->validate_value(vtor, value);
+ CU_ASSERT_PTR_NULL_FATAL(validated_value);
+
+ if (validated_value && validated_value != value)
+ etch_object_destroy(validated_value);
+
+ /* validator destructor is benign if validator is marked cached
+ * however a struct validator is not cached */
+ etch_object_destroy(vtor);
+}
+
+
+/**
+ * test_struct_good_values()
+ */
+static void test_struct_good_values(void)
+{
+ etch_type* typeabc = new_type(L"abc");
+ etch_nativearray* array_of_struct = NULL;
+ const int NUMDIMS0 = 0, NUMDIMS1 = 1, NUMDIMS2 = 2, DIMSIZE4 = 4;
+
+ etch_structvalue* sv = new_structvalue(typeabc, 0);
+ do_struct_goodvalue_test(NUMDIMS0, ETCH_XTRNL_TYPECODE_CUSTOM, (etch_object*) sv, typeabc, FALSE);
+
+ /* following tests create an empty array of structvalue objects */
+ array_of_struct = new_etch_nativearray_of(((etch_object*)sv)->obj_type, ((etch_object*)sv)->class_id, NUMDIMS1, DIMSIZE4, 0, 0);
+ do_struct_goodvalue_test(NUMDIMS1, ETCH_XTRNL_TYPECODE_ARRAY, (etch_object*) array_of_struct, typeabc, FALSE);
+ etch_object_destroy(array_of_struct);
+
+ array_of_struct = new_etch_nativearray_of(((etch_object*)sv)->obj_type, ((etch_object*)sv)->class_id, NUMDIMS2, DIMSIZE4, DIMSIZE4, 0);
+ do_struct_goodvalue_test(NUMDIMS2, ETCH_XTRNL_TYPECODE_ARRAY, (etch_object*) array_of_struct, typeabc, FALSE);
+ etch_object_destroy(array_of_struct);
+
+ etch_object_destroy(sv);
+ etch_object_destroy(typeabc); /* in practice this would have been a static type */
+ etchvtor_clear_cache(); /* not needed since struct validator not cached */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_struct_bad_values()
+ */
+static void test_struct_bad_values(void)
+{
+ etch_type* type_expected = new_type(L"abc");
+ etch_type* type_unexpect = new_type(L"def");
+ etch_nativearray* array_of_struct = NULL;
+ const int NUMDIMS0 = 0, NUMDIMS1 = 1, NUMDIMS2 = 2, NUMDIMS3 = 3, DIMSIZE4 = 4;
+
+ etch_structvalue* sv = new_structvalue(type_expected, 0);
+ etch_structvalue* svbogus = new_structvalue(type_unexpect, 0);
+ etch_int32* intobj = new_int32(1);
+ etch_object* objobj = (etch_object*)intobj;
+
+ do_struct_badvalue_test(0, (etch_object*) intobj, type_expected);
+ do_struct_badvalue_test(0, objobj, type_expected);
+ do_struct_badvalue_test(0, (etch_object*) svbogus, type_expected);
+ do_struct_badvalue_test(0, (etch_object*) sv, type_unexpect);
+
+ do_struct_badvalue_test(1, (etch_object*) intobj, type_expected);
+ do_struct_badvalue_test(1, objobj, type_expected);
+ do_struct_badvalue_test(1, (etch_object*) svbogus, type_expected);
+ do_struct_badvalue_test(1, (etch_object*) sv, type_unexpect);
+
+ /* following tests create an empty array of structvalue objects */
+ array_of_struct = new_etch_nativearray_of(((etch_object*)sv)->obj_type, ((etch_object*)sv)->class_id, NUMDIMS1, DIMSIZE4, 0, 0);
+ do_struct_badvalue_test(NUMDIMS0, (etch_object*) array_of_struct, type_expected);
+ etch_object_destroy(array_of_struct);
+
+ array_of_struct = new_etch_nativearray_of(((etch_object*)sv)->obj_type, ((etch_object*)sv)->class_id, NUMDIMS2, DIMSIZE4, DIMSIZE4, 0);
+ do_struct_badvalue_test(NUMDIMS1, (etch_object*) array_of_struct, type_expected);
+ do_struct_badvalue_test(NUMDIMS0, (etch_object*) array_of_struct, type_expected);
+ etch_object_destroy(array_of_struct);
+
+ array_of_struct = new_etch_nativearray_of(((etch_object*)sv)->obj_type, ((etch_object*)sv)->class_id, NUMDIMS3, DIMSIZE4, DIMSIZE4, DIMSIZE4);
+ do_struct_badvalue_test(NUMDIMS2, (etch_object*) array_of_struct, type_expected);
+ do_struct_badvalue_test(NUMDIMS1, (etch_object*) array_of_struct, type_expected);
+ do_struct_badvalue_test(NUMDIMS0, (etch_object*) array_of_struct, type_expected);
+ etch_object_destroy(array_of_struct);
+
+ etch_object_destroy(objobj);
+ etch_object_destroy(sv);
+ etch_object_destroy(svbogus);
+ etch_object_destroy(type_expected); /* in real world these are static types */
+ etch_object_destroy(type_unexpect);
+ etchvtor_clear_cache(); /* however struct validator is not cached */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_combo_validator_1()
+ */
+static void test_combo_validator_1(void)
+{
+ int result = 0;
+ etch_validator *vtor1 = 0, *vtor2 = 0, *vtor3 = 0;
+ create_testobjects();
+
+ /* one cached validator */
+ vtor1 = etchvtor_boolean_get(0);
+ vtor2 = NULL;
+ vtor3 = new_combo_validator(vtor1, vtor2);
+ result = is_etch_combo_validator(vtor3);
+ CU_ASSERT_EQUAL_FATAL(result, TRUE);
+ result = vtor3->validate(vtor3, (etch_object*) objbool_true);
+ CU_ASSERT_EQUAL(result,0);
+ etch_object_destroy(vtor3);
+
+ /* one cached validator */
+ vtor1 = NULL;
+ vtor2 = etchvtor_boolean_get(0);
+ vtor3 = new_combo_validator(vtor1, vtor2);
+ result = vtor3->validate(vtor3, (etch_object*) objbool_true);
+ CU_ASSERT_EQUAL(result,0);
+ etch_object_destroy(vtor3);
+
+ /* two cached validators */
+ vtor1 = etchvtor_boolean_get(0);
+ vtor2 = etchvtor_int32_get(0);
+ vtor3 = new_combo_validator(vtor1, vtor2);
+ result = vtor3->validate(vtor3, (etch_object*) objint_three);
+ CU_ASSERT_EQUAL(result,0);
+ etch_object_destroy(vtor3);
+
+ destroy_testobjects();
+ etchvtor_clear_cache(); /* destroy cached validators */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_combo_validator_2()
+ */
+static void test_combo_validator_2(void)
+{
+ int result = 0;
+ etch_validator *vtor1 = 0, *vtor2 = 0, *vtor3 = 0, *vtor4 = 0, *vtor5 = 0;
+ create_testobjects();
+
+ /* chain 1 of cached validators and combos */
+ vtor1 = etchvtor_boolean_get(0);
+ vtor2 = etchvtor_boolean_get(0);
+ vtor3 = new_combo_validator(vtor1, vtor2);
+ vtor4 = etchvtor_boolean_get(0);
+ vtor5 = new_combo_validator(vtor4, vtor3);
+ result = vtor5->validate(vtor5, (etch_object*) objbool_true);
+ CU_ASSERT_EQUAL(result,0);
+ etch_object_destroy(vtor5);
+
+ /* chain 2 of cached validators and combos */
+ vtor1 = etchvtor_boolean_get(0);
+ vtor2 = etchvtor_boolean_get(0);
+ vtor3 = new_combo_validator(vtor1, vtor2);
+ vtor4 = etchvtor_boolean_get(0);
+ vtor5 = new_combo_validator(vtor3, vtor4);
+ result = vtor5->validate(vtor5, (etch_object*) objbool_true);
+ CU_ASSERT_EQUAL(result,0);
+ etch_object_destroy(vtor5);
+
+ destroy_testobjects();
+ etchvtor_clear_cache(); /* destroy cached validators */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_combo_validator_3()
+ */
+static void test_combo_validator_3(void)
+{
+ int result = 0;
+ etch_validator *vtor1 = 0, *vtor2 = 0, *vtor3 = 0, *vtor4 = 0, *vtor5 = 0;
+ create_testobjects();
+
+ /* chain 1 of cached and non-cached validators and combos */
+ vtor1 = etchvtor_boolean_get(0);
+ vtor2 = etchvtor_int32_get(0);
+ vtor3 = new_combo_validator(vtor1, vtor2);
+ vtor4 = etchvtor_struct_get(testtype, 0);
+ vtor5 = new_combo_validator(vtor4, vtor3);
+ result = vtor5->validate(vtor5, (etch_object*) objint_three);
+ CU_ASSERT_EQUAL(result, 0);
+ etch_object_destroy(vtor5);
+
+ /* chain 2 of cached and non-cached validators and combos */
+ vtor1 = etchvtor_int32_get(0);
+ vtor2 = etchvtor_boolean_get(0);
+ vtor3 = new_combo_validator(vtor1, vtor2);
+ vtor4 = etchvtor_struct_get(testtype, 0);
+ vtor5 = new_combo_validator(vtor3, vtor4);
+ result = vtor5->validate(vtor5, (etch_object*) objint_three);
+ CU_ASSERT_EQUAL(result, 0);
+ etch_object_destroy(vtor5);
+
+ destroy_testobjects();
+ etchvtor_clear_cache(); /* destroy cached validators */
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_validator_suite()
+{
+ CU_pSuite ps = CU_add_suite("validator test suite", init_suite, clean_suite);
+
+ CU_add_test(ps, "test boolean validator", test_boolean_validator);
+ CU_add_test(ps, "test validate boolean arrays", test_array_boolean);
+ CU_add_test(ps, "test validate byte arrays", test_array_byte);
+ CU_add_test(ps, "test validate int16 arrays", test_array_int16);
+ CU_add_test(ps, "test validate int32 arrays", test_array_int32);
+ CU_add_test(ps, "test validate int64 arrays", test_array_int64);
+ CU_add_test(ps, "test validate float arrays", test_array_float);
+ CU_add_test(ps, "test validate double arrays", test_array_double);
+ CU_add_test(ps, "test validate etch_string* arrays", test_array_string);
+ CU_add_test(ps, "test validate etch_object* arrays", test_array_object);
+ CU_add_test(ps, "test struct element validator", test_struct_element_validator);
+ CU_add_test(ps, "test struct good values", test_struct_good_values);
+ CU_add_test(ps, "test struct bad values", test_struct_bad_values);
+ CU_add_test(ps, "test combo validator 1", test_combo_validator_1);
+ CU_add_test(ps, "test combo validator 2", test_combo_validator_2);
+ CU_add_test(ps, "test combo validator 3", test_combo_validator_3);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/support/test_remote.c b/binding-c/runtime/c/src/test/support/test_remote.c
new file mode 100644
index 0000000..99bd47b
--- /dev/null
+++ b/binding-c/runtime/c/src/test/support/test_remote.c
@@ -0,0 +1,1180 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_remote.c
+ * test remote, delivery service, etc.
+ */
+
+#include "etch_runtime.h"
+#include "etch_svcobj_masks.h"
+#include "etch_transport.h"
+#include "etch_thread.h"
+#include "etch_stub.h"
+#include "etch_remote.h"
+#include "etch_default_value_factory.h"
+#include "etch_plain_mailbox.h"
+#include "etch_plain_mailbox_manager.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_map.h"
+#include "etch_log.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#ifdef _WIN32
+#pragma message ( "this testsuite is not active" )
+#else
+#warning "this testsuite is not active"
+#endif
+
+#define IS_DEBUG_CONSOLE FALSE
+
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+#if 0
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+#define THISTEST_WHO_VALUE 0x5151
+
+#define TEST_MSGID 0x1001
+#define TEST_QDELAYMS 1000
+#define TEST_MBOX_LIFETIME_UNTIL_CLOSE 0
+#define TEST_LIFETIME_ONE_SECOND 1000
+#define TEST_MAXMSGS_ONE 1
+
+static unsigned short CLASSID_MY_VF;
+static unsigned short CLASSID_MY_VF_VTAB;
+static unsigned short CLASSID_MY_VF_IMPL;
+static unsigned short CLASSID_MY_REMOTEIMPL;
+static unsigned short CLASSID_MY_IMPLBASE;
+static unsigned short CLASSID_MY_REMOTEBASE;
+static unsigned short CLASSID_MY_RESULT;
+
+typedef enum whats
+{ TRANSPORT_MESSAGE = 1, TRANSPORT_QUERY, TRANSPORT_CONTROL, TRANSPORT_NOTIFY,
+ BEGIN_CALL, END_CALL
+} whats;
+
+static etch_resources* g_my_resources;
+static default_value_factory* g_my_vf;
+static etch_plainmailbox* g_my_mbox;
+static i_mailbox* g_my_ibox;
+static etch_plainmailboxmgr* g_my_mboxmgr;
+static i_delivery_service* g_my_ds;
+static etch_type* g_mt_foo;
+static etch_type* g_mt_bar;
+
+static etch_who* gds_who;
+static int64 gds_messageid;
+static int gds_what;
+static int gds_rtypeid;
+static int gds_eventval;
+static int gds_queryval;
+static int gds_controlval;
+static int gds_valueval;
+
+static i_delivery_service* new_my_delivery_service();
+static int my_pmboxmgr_session_notify (etch_plainmailboxmgr*, etch_event*);
+static default_value_factory* new_fake_valuefactory();
+static char* LOGSRC = "TEST";
+
+
+/**
+ * new_my_resources()
+ * resources map constructor
+ */
+static etch_resources* new_my_resources(void* valuefactory)
+{
+ etch_resources* resx = get_etch_transport_resources(NULL);
+ etch_resources_add(resx, ETCH_RESXKEY_MSGIZER_VALUFACT, valuefactory);
+ return resx;
+}
+
+
+/**
+ * setup_this_test()
+ * set up an individual unit test
+ */
+static int setup_this_test()
+{
+ CLASSID_MY_VF = get_dynamic_classid();
+ CLASSID_MY_VF_VTAB = get_dynamic_classid();
+ CLASSID_MY_VF_IMPL = get_dynamic_classid();
+ #if(IS_DEBUG_CONSOLE)
+ printf("\n");
+ #endif
+
+ g_mt_foo = new_static_type(L"foo");
+ g_mt_bar = new_static_type(L"bar");
+
+ g_my_vf = new_fake_valuefactory();
+ set_etchobj_static_all(g_my_vf); /* so resources map will not destroy */
+
+ /* get resources map populated with transport resources such as thread pools */
+ g_my_resources = new_my_resources(g_my_vf);
+
+ g_my_ds = new_my_delivery_service ();
+
+ /* the ids itm object belongs to the ds transport which is the mailbox manager */
+ g_my_mboxmgr = g_my_ds->transport->thisx;
+ CU_ASSERT_FATAL(is_etch_mailboxmgr(g_my_mboxmgr));
+
+ /* register a new mailbox with the manager */
+ g_my_mbox = new_mailbox (g_my_mboxmgr->imanager, TEST_MSGID,
+ TEST_QDELAYMS, TEST_MBOX_LIFETIME_UNTIL_CLOSE, TEST_MAXMSGS_ONE);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_my_mbox);
+ g_my_ibox = g_my_mbox->imailbox;
+ g_my_mboxmgr->session_notify = my_pmboxmgr_session_notify;
+
+ return 0;
+}
+
+
+/**
+ * teardown_this_test()
+ * tear down an individual unit test
+ */
+static void teardown_this_test()
+{
+ if (g_my_mboxmgr)
+ if (g_my_mbox)
+ etch_object_destroy(g_my_mbox);
+
+ if (g_my_resources)
+ { /* we did a set_etchobj_static_all() on the g_my_vf value factory
+ * and as a result the map will not destroy it. if we had not done
+ * so, the vf would have been destroyed with the resources map. */
+ etch_object_destroy(g_my_resources);
+ }
+
+ if (g_my_vf)
+ { /* we clear the set_etchobj_static_all() on the g_my_vf value factory
+ * and as a result we can then destroy it */
+ clear_etchobj_static_all(g_my_vf);
+ etch_object_destroy(g_my_vf);
+ }
+
+ etch_object_destroy(g_my_ds);
+
+ destroy_static_type(g_mt_foo);
+ destroy_static_type(g_mt_bar);
+
+ gds_what = 0;
+ gds_who = NULL;
+ g_my_vf = NULL;
+ g_my_ds = NULL;
+ g_my_mbox = NULL;
+ g_my_ibox = NULL;
+ g_my_mboxmgr = NULL;
+
+ g_mt_foo = g_mt_bar = NULL;
+ gds_eventval= gds_queryval = gds_controlval = gds_valueval = 0;
+ g_my_resources = NULL;
+ gds_messageid = 0;
+ gds_rtypeid = 0;
+
+ etchvf_free_builtins();
+}
+
+/* - - - - - - - - - -
+ * mailbox manager
+ * - - - - - - - - - -
+ */
+
+static int my_pmboxmgr_session_notify (etch_plainmailboxmgr* mgr, etch_event* evt)
+{
+ switch(evt->value)
+ { case ETCHEVT_SESSION_DOWN: pmboxmgr_unregister_all(mgr);
+ }
+ // ETCHOBJ_DESTROY();
+ etch_object_destroy(evt);
+ evt = NULL;
+
+ return 0;
+}
+
+
+/* - - - - - - - - - -
+ * remote object
+ * - - - - - - - - - -
+ */
+
+/**
+ * my_remoteobj
+ * xxxx_remote_either
+ * this represents for this test what would be a service remote_server or remote_client in practice.
+ */
+typedef struct my_remoteobj
+{
+ etch_object object;
+
+ i_xxxx_either* xxxx_either_base; /* owned server or client */
+ xxxx_remote* remote_base; /* owned */
+ void* either_factory; /* owned */
+ default_value_factory* vf; /* owned by base */
+
+ /* note that the transport interface is accessed via the remote_base */
+
+} my_remoteobj;
+
+
+
+/* - - - - - - - - - -
+ * value factory
+ * - - - - - - - - - -
+ */
+
+/**
+ * my_valufactory_impl
+ * value factory instance data object
+ */
+typedef struct my_valufactory_impl
+{
+ etch_object object;
+
+} my_valufactory_impl;
+
+
+/**
+ * destroy_my_valufactory_impl()
+ * destructor for inheriting value factory instance data
+ */
+static int destroy_my_valufactory_impl(my_valufactory_impl* impl)
+{
+ if (NULL == impl) return -1;
+
+ if (!is_etchobj_static_content(impl))
+ {
+ /* no custom types to destroy - they are global for this test */
+ }
+
+ return destroy_objectex((etch_object*) impl);
+}
+
+
+/**
+ * new_my_valufactory_impl()
+ * constructor for our value factory's instance data
+ */
+static my_valufactory_impl* new_my_valufactory_impl()
+{
+ unsigned short class_id = CLASSID_MY_VF_IMPL? CLASSID_MY_VF_IMPL:
+ (CLASSID_MY_VF_IMPL = get_dynamic_classid());
+
+ my_valufactory_impl* impl = (my_valufactory_impl*) new_object
+ (sizeof(my_valufactory_impl), ETCHTYPEB_VALUEFACTIMP, class_id);
+
+ ((etch_object*)impl)->destroy = destroy_my_valufactory_impl;
+
+ return impl;
+}
+
+
+/**
+ * new_fake_valuefactory()
+ */
+static default_value_factory* new_fake_valuefactory()
+{
+ my_valufactory_impl* impl = NULL;
+ etchparentinfo* inheritlist = NULL;
+ const unsigned short classid_vf = get_dynamic_classid_unique(&CLASSID_MY_VF);
+ const unsigned short classid_vf_vtab = get_dynamic_classid_unique(&CLASSID_MY_VF_VTAB);
+
+ g_my_vf = new_default_value_factory(NULL, NULL);
+
+ /* ensure parent type keys exist in the (one-based) inheritance list.
+ * parent class of our custom vf is default_value_factory.
+ * inheritance list is used by validators and object assignment logic.
+ */
+ inheritlist = get_vtab_inheritance_list((etch_object*)g_my_vf, 2, 1, classid_vf_vtab);
+ inheritlist[1].obj_type = ETCHTYPEB_VALUEFACTORY;
+ inheritlist[1].class_id = CLASSID_VALUEFACTORY; /* parent class */
+ ((etch_object*)g_my_vf)->class_id = classid_vf; /* our class */
+
+ /* instantiate the custom vf's instance data and assign it to the vf.
+ * the impl comprises all data specific to the inheriting class, including
+ * data and methods if any. the default value factory destructor will call
+ * the destructor on the vf's impl object.
+ */
+ impl = new_my_valufactory_impl();
+ g_my_vf->impl = (etch_object*) impl;
+ ((struct i_value_factory*)((etch_object*)g_my_vf)->vtab)->add_type(g_my_vf, g_mt_foo);
+ ((struct i_value_factory*)((etch_object*)g_my_vf)->vtab)->add_type(g_my_vf, g_mt_bar);
+
+ /* set msgid validator so we can set and retrieve message IDs */
+ etchtype_put_validator(g_mt_foo, builtins._mf__message_id, (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(g_mt_bar, builtins._mf__message_id, (etch_object*) etchvtor_int64_get(0));
+
+ return g_my_vf;
+}
+
+
+/* - - - - - - - - - -
+ * delivery service
+ * - - - - - - - - - -
+ */
+
+/**
+ * myds_begincall()
+ * override for i_delivery_service.begin_call().
+ * typedef int (*etch_delivsvc_begincall)(void* thisx, etch_message*, void** out);
+ * @param msg caller relinquishes on success, retains on failure
+ * @param out mailbox interface returned on success
+ * @return 0 success, or -1 failure. new mailbox return in out parameter.
+ */
+static int myds_begincall (i_delivery_service* thisx, etch_message* msg, void** out)
+{
+ etch_int64* msgid = NULL;
+ assert(out);
+ *out = g_my_ibox;
+ gds_what = BEGIN_CALL;
+ msgid = message_get_id(msg);
+ gds_messageid = msgid? msgid->value: 0;
+ etch_object_destroy(msg);
+ msg = NULL;
+
+ return 0;
+}
+
+
+/**
+ * myds_endcall()
+ * override for i_delivery_service.end_call().
+ * typedef int (*etch_delvisvc_endcall)(void* thisx, i_mailbox*, etch_type*, void** out);
+ * message response received. close mailbox and return response.
+ * @param mbox the current mailbox (interface), caller retains.
+ * @param response_type type of the response message, caller retains.
+ * @param out pointer to caller's location to receive the message response object.
+ * @return 0 success, -1 failure. response object returned via out parameter.
+ * @remarks assumed that the reply message and its wrapper are destroyed with the mailbox.
+ */
+static int myds_endcall (i_delivery_service* thisx, i_mailbox* mbox, etch_type* rtype, void** out)
+{
+ etch_int32* resultobj = new_int32(CLASSID_MY_RESULT);
+ ((etch_object*)resultobj)->class_id = CLASSID_MY_RESULT;
+ gds_what = END_CALL;
+ gds_rtypeid = rtype->id;
+ assert(out);
+ *out = resultobj;
+ return 0;
+}
+
+
+/* - - - - - - - - - - - - - - - - -
+ * delivery service i_sessionmessage
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/* this is the i_delivery_service implementation of i_sessionmessage,
+ * distinct from the transport.session's implementation of i_sessionmessage
+ * which is implemented externally and set via set_session().
+ */
+
+/**
+ * myds_session_message()
+ * override for i_delivery_service.ism.session_message().
+ * @param whofrom caller retains, can be null.
+ * @param msg caller relinquishes
+ * @return 0 (message handled), or -1 (error, closed, or timeout)
+ */
+static int myds_session_message (etch_tcp_delivery_service* thisx, etch_who* whofrom, etch_message* msg)
+{
+ etch_object_destroy(msg);
+ msg = NULL;
+
+ return -1;
+}
+
+
+/**
+ * myds_session_control()
+ * override for i_delivery_service.ism.session_control().
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+static int myds_session_control (etch_tcp_delivery_service* thisx, etch_event* control, etch_object* value)
+{
+ etch_object_destroy(control);
+ control = NULL;
+
+ etch_object_destroy(value);
+ value = NULL;
+
+ return -1;
+}
+
+
+/**
+ * myds_session_notify()
+ * override for i_delivery_service.ism.session_notify().
+ * @param evt event, caller relinquishes.
+ */
+static int myds_session_notify (etch_tcp_delivery_service* thisx, etch_event* evt)
+{
+ etch_object_destroy(evt);
+ evt = NULL;
+
+ return -1;
+}
+
+
+/**
+ * myds_session_query()
+ * override for i_delivery_service.ism.session_query().
+ * @param query, caller relinquishes.
+ */
+static etch_object* myds_session_query (void* data, etch_query* query)
+{
+ etch_tcp_delivery_service* thisx = (etch_tcp_delivery_service*)data;
+
+ etch_object_destroy(query);
+ query = NULL;
+ return NULL;
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * delivery service i_transportmessage
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * myds_transport_message()
+ * override for i_delivery_service.itm.transport_message().
+ * @param whoto recipient - caller retains
+ * @param message caller relinquishes on success, retains on failure.
+ * @return 0 success, -1 error.
+ */
+static int myds_transport_message (etch_tcp_delivery_service* thisx, etch_who* whoto, etch_message* msg)
+{
+ etch_int64* msgid = NULL;
+ gds_what = TRANSPORT_MESSAGE;
+ gds_who = whoto;
+ msgid = message_get_id(msg);
+ gds_messageid = msgid? msgid->value: 0;
+ etch_object_destroy(msg);
+ msg = NULL;
+
+ return 0;
+}
+
+
+/**
+ * myds_transport_control()
+ * override for i_delivery_service.itm.transport_control()
+ * @param control caller relinquishes.
+ * @param value caller relinquishes.
+ */
+static int myds_transport_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_tcp_delivery_service* thisx = (etch_tcp_delivery_service*)data;
+ etch_int32* i = NULL;
+ assert(control);
+ gds_what = TRANSPORT_CONTROL;
+ gds_controlval = control->value;
+ i = (etch_int32*)value;
+ gds_valueval = i? i->value: -1;
+ etch_object_destroy(control);
+ control = NULL;
+
+ etch_object_destroy(value);
+ value = NULL;
+
+ return 0;
+}
+
+
+/**
+ * myds_transport_notify()
+ * override for i_delivery_service.itm.transport_notify()
+ * i_transportmessage::transport_notify override.
+ * @param evt, caller relinquishes.
+ */
+static int myds_transport_notify (etch_tcp_delivery_service* thisx, etch_event* evt)
+{
+ assert(evt);
+ gds_what = TRANSPORT_NOTIFY;
+ gds_eventval = evt->value;
+ etch_object_destroy(evt);
+ return 0;
+}
+
+
+/**
+ * myds_transport_query()
+ * override for i_delivery_service.itm.transport_query()
+ * i_transportmessage::transport_query override.
+ * @param query, caller relinquishes.
+ */
+static etch_object* myds_transport_query (etch_tcp_delivery_service* thisx, etch_query* query)
+{
+ etch_int32* resultobj = new_int32(CLASSID_MY_RESULT);
+ ((etch_object*)resultobj)->class_id = CLASSID_MY_RESULT;
+ gds_what = TRANSPORT_QUERY;
+ gds_queryval = query->value;
+ etch_object_destroy(query);
+ return (etch_object*) resultobj;
+}
+
+/**
+ * myds_get_session()
+ * override for i_delivery_service.itm.get_session()
+ * i_transportmessage::get_session override.
+ */
+static struct i_sessionmessage* myds_get_session (void* data)
+{
+ etch_tcp_delivery_service* thisx = (etch_tcp_delivery_service*)data;
+ assert(FALSE);
+ return NULL;
+}
+
+
+/**
+ * myds_set_session()
+ * override for i_delivery_service.itm.set_session()
+ * i_transportmessage::set_session override.
+ */
+static void myds_set_session (etch_tcp_delivery_service* thisx, i_sessionmessage* newsession)
+{
+ assert(FALSE);
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * delivery service construction
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_my_delivery_service()
+ */
+static i_delivery_service* new_my_delivery_service()
+{
+ etch_tcp_connection* nullconnection = NULL;
+ etch_tcp_delivery_service* delsvc = NULL;
+ i_sessionmessage* ism = NULL;
+ i_transportmessage* itm = NULL;
+ etch_client_factory* impl_factory = new_client_factory (NULL, NULL, NULL);
+
+ i_delivery_service* ids = new_etch_transport(L"http://www.cisco.com:9999/cuae",
+ (etch_factory_params*) impl_factory, nullconnection);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(ids);
+
+ delsvc = ids->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(delsvc);
+ CU_ASSERT_EQUAL_FATAL(is_etch_deliverysvc(delsvc), TRUE);
+
+ ism = ids->ism;
+ itm = ids->itm;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(ism);
+ CU_ASSERT_EQUAL_FATAL(is_etch_sessionmsg(ism), TRUE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(itm);
+ CU_ASSERT_EQUAL_FATAL(is_etch_transportmsg(itm), TRUE);
+
+ /* override delivery service i_sessionmessage to implementations herein */
+ ids->begin_call = delsvc->begin_call = myds_begincall;
+ ids->end_call = delsvc->end_call = myds_endcall;
+ ism->session_message = delsvc->session_message = myds_session_message;
+ ism->session_control = delsvc->session_control = myds_session_control;
+ ism->session_notify = delsvc->session_notify = myds_session_notify;
+ ism->session_query = delsvc->session_query = myds_session_query;
+
+ /* override delivery service i_transportmessage to implementations herein.
+ * note that we swap out the virtuals, but the not the itm object, which
+ * is the ds transport, which is the mailbox manager's itm. the mailbox
+ * manager owns it and will destroy it when destroyed during destruction
+ * of the delivery service.
+ */
+ itm->transport_message = delsvc->transport_message = myds_transport_message;
+ itm->transport_control = delsvc->transport_control = myds_transport_control;
+ itm->transport_notify = delsvc->transport_notify = myds_transport_notify;
+ itm->transport_query = delsvc->transport_query = myds_transport_query;
+ itm->get_session = delsvc->get_session = myds_get_session;
+ itm->set_session = delsvc->set_session = myds_set_session;
+
+ return ids;
+}
+
+
+/* - - - - - - - - - - - - - - - - -
+ * remote androgynous object
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * destroy_my_remoteobj()
+ * destructor for our remote object
+ */
+static int destroy_my_remoteobj(my_remoteobj* thisx)
+{
+ etch_object_destroy(thisx->xxxx_either_base);
+ thisx->xxxx_either_base = NULL;
+
+ etch_object_destroy(thisx->remote_base);
+ thisx->remote_base = NULL;
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+/**
+ * destroy_my_clientorserver_impl()
+ * destructor for our remoteobj.xxxx_either_base
+ */
+static int destroy_my_clientorserver_impl(i_xxxx_either* thisx)
+{
+ etch_free(thisx->iobjsession);
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * new_my_clientorserver_impl()
+ * instatiate my_remoteobj.xxxx_either_base
+ */
+static i_xxxx_either* new_my_clientorserver_impl(my_remoteobj* thisx)
+{
+ i_xxxx_either* either_base = (i_xxxx_either*) new_object (sizeof(i_xxxx_either),
+ ETCHTYPEB_EXESERVERBASE, get_dynamic_classid_unique(&CLASSID_MY_IMPLBASE));
+
+ ((etch_object*)either_base)->destroy = destroy_my_clientorserver_impl;
+
+ either_base->thisx = (etch_object*) thisx;
+
+ { /* populate as much of xxxx_either_impl as we need */
+ i_objsession* ios = new_default_objsession_interface (thisx);
+ either_base->iobjsession = ios;
+ either_base->_session_control = ios->_session_control;
+ either_base->_session_notify = ios->_session_notify;
+ either_base->_session_query = ios->_session_query;
+ }
+
+ return either_base;
+}
+
+
+/**
+ * new_my_remote_base
+ * instantiates remote base.
+ * @param ids delivery service -- caller retains.
+ * @param vf default value factory -- caller retains.
+ * @param ixxxx service interface -- caller retains.
+ */
+static xxxx_remote* new_my_remote_base (void* thisx,
+ i_delivery_service* ids, etch_value_factory* vf, etch_object* ixxxx)
+{
+ xxxx_remote* remote = new_etch_remote_base (thisx, ETCH_DEFSIZE,
+ get_dynamic_classid_unique(&CLASSID_MY_REMOTEBASE), ids, vf, ixxxx);
+
+ return remote;
+}
+
+
+/**
+ * new_my_remote_client_or_server()
+ * instatiate and return an implementing object for the remote base.
+ */
+static my_remoteobj* new_my_remote_client_or_server (void* thisx, i_delivery_service* ids, etch_value_factory* vf)
+{
+ /* for these tests it doesn't matter whether we use obj_type of client or server */
+ my_remoteobj* remoteobj = (my_remoteobj*) new_object (sizeof(my_remoteobj),
+ ETCHTYPEB_REMOTESERVER, get_dynamic_classid_unique(&CLASSID_MY_REMOTEIMPL));
+
+ ((etch_object*)remoteobj)->destroy = destroy_my_remoteobj;
+ remoteobj->remote_base = new_my_remote_base (remoteobj, ids, vf, NULL);
+ remoteobj->xxxx_either_base = new_my_clientorserver_impl(remoteobj);
+
+ return remoteobj;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * unit tests
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_setup()
+ * test instantiation and teardown of test components such as delivery service,
+ * value factory, etc.
+ */
+static void test_setup(void)
+{
+ setup_this_test();
+
+ do
+ {
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_new_message()
+ */
+static void test_new_message(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+
+ my_remoteobj* myremote = new_my_remote_client_or_server (NULL,
+ g_my_ds, (etch_value_factory*) g_my_vf);
+
+ etch_remote* remote = myremote->remote_base;
+
+ /* this call goes through etchremote_new_message() */
+ etch_message* newmsg = remote->new_message(remote, g_mt_foo);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newmsg);
+
+ result = is_equal_types(message_type(newmsg), g_mt_foo);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ CU_ASSERT_PTR_EQUAL(newmsg->vf, g_my_vf);
+
+ etch_object_destroy(newmsg);
+ etch_object_destroy(myremote);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_send()
+ */
+static void test_send(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, MSGIDVAL = 54321;
+
+ my_remoteobj* myremote = new_my_remote_client_or_server (NULL,
+ g_my_ds, (etch_value_factory*) g_my_vf);
+
+ etch_remote* remote = myremote->remote_base;
+ etch_message* newmsg = remote->new_message (myremote->remote_base, g_mt_foo);
+ message_set_id (newmsg, new_int64(MSGIDVAL));
+
+ /* this call goes through delivery service.transport_message,
+ * which we overrode to myds_transport_message() above.
+ */
+ result = remote->send (remote, newmsg); /* send message */
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(TRANSPORT_MESSAGE, gds_what);
+ CU_ASSERT_EQUAL(gds_messageid, MSGIDVAL);
+
+ if (0 != result) /* message was relinquished on send() success */
+ {
+ etch_object_destroy(newmsg);
+ newmsg = NULL;
+
+ }
+
+ ((etch_object*)myremote)->destroy(myremote);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_begincall()
+ */
+static void test_begincall(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, MSGIDVAL = 65432;
+ i_mailbox* outmbox = NULL;
+
+ my_remoteobj* myremote = new_my_remote_client_or_server (NULL,
+ g_my_ds, (etch_value_factory*) g_my_vf);
+
+ etch_remote* remote = myremote->remote_base;
+ etch_message* newmsg = remote->new_message (myremote->remote_base, g_mt_foo);
+ message_set_id (newmsg, new_int64(MSGIDVAL));
+
+ /* this call goes through delivery service.begin_call,
+ * which we overrode to myds_begincall() above.
+ */
+ result = remote->begin_call (remote, newmsg, &outmbox);
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(BEGIN_CALL, gds_what);
+ CU_ASSERT_EQUAL(gds_messageid, MSGIDVAL);
+ CU_ASSERT_PTR_EQUAL(g_my_ibox, outmbox);
+
+ if (0 != result) /* message was relinquished on begin_call() success */
+ {
+ etch_object_destroy(newmsg);
+ newmsg = NULL;
+ }
+
+ etch_object_destroy(myremote);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_endcall()
+ */
+static void test_endcall(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, resultval = 0;
+ etch_object* resultobj = NULL;
+
+ my_remoteobj* myremote = new_my_remote_client_or_server (NULL,
+ g_my_ds, (etch_value_factory*) g_my_vf);
+
+ etch_remote* remote = myremote->remote_base;
+
+ /* this call goes through delivery service.end_call,
+ * which we overrode to myds_endcall() above.
+ */
+ result = remote->end_call (remote, g_my_ibox, g_mt_bar, &resultobj);
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(END_CALL, gds_what);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(resultobj);
+ CU_ASSERT_EQUAL(((etch_object*)resultobj)->class_id, CLASSID_MY_RESULT);
+ CU_ASSERT_EQUAL(g_mt_bar->id, gds_rtypeid);
+
+ etch_object_destroy(resultobj);
+ resultobj = NULL;
+
+
+ etch_object_destroy(myremote);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_query()
+ */
+static void test_transport_query(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, resultval = 0, THISQUERYVAL = 76543;
+ etch_object* resultobj = NULL;
+
+ my_remoteobj* myremote = new_my_remote_client_or_server (NULL,
+ g_my_ds, (etch_value_factory*) g_my_vf);
+ etch_remote* remote = myremote->remote_base;
+
+ /* this call goes through delivery service.transport_query,
+ * which we overrode to myds_transport_query() above.
+ */
+ resultobj = remote->transport_query (remote, new_etch_query(0, THISQUERYVAL));
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(resultobj);
+ CU_ASSERT_EQUAL(((etch_object*)resultobj)->class_id, CLASSID_MY_RESULT);
+ CU_ASSERT_EQUAL(TRANSPORT_QUERY, gds_what);
+
+ etch_object_destroy(resultobj);
+ resultobj = NULL;
+
+ etch_object_destroy(myremote);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_control()
+ */
+static void test_transport_control(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, resultval = 0, THISCTLVAL = 87654, THISVALVAL = 98765;
+
+ my_remoteobj* myremote = new_my_remote_client_or_server (NULL,
+ g_my_ds, (etch_value_factory*) g_my_vf);
+ etch_remote* remote = myremote->remote_base;
+
+ /* this call goes through delivery service.transport_control,
+ * which we overrode to myds_transport_control() above.
+ */
+ result = remote->transport_control (remote, new_etch_control(0, THISCTLVAL), new_int32(THISVALVAL));
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(TRANSPORT_CONTROL, gds_what);
+ CU_ASSERT_EQUAL(gds_controlval, THISCTLVAL);
+ CU_ASSERT_EQUAL(gds_valueval, THISVALVAL);
+
+ etch_object_destroy(myremote);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_notify()
+ */
+static void test_transport_notify(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, resultval = 0, THISEVTVAL = 45678;
+
+ my_remoteobj* myremote = new_my_remote_client_or_server (NULL,
+ g_my_ds, (etch_value_factory*) g_my_vf);
+ etch_remote* remote = myremote->remote_base;
+
+ /* this call goes through delivery service.transport_notify,
+ * which we overrode to myds_transport_notify() above.
+ */
+ result = remote->transport_notify (remote, new_etch_event(0, THISEVTVAL));
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(TRANSPORT_NOTIFY, gds_what);
+ CU_ASSERT_EQUAL(gds_eventval, THISEVTVAL);
+
+ etch_object_destroy(myremote);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_start_waitup()
+ */
+static void test_start_waitup(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, resultval = 0, THISWAITMS = 3000;
+
+ my_remoteobj* myremote = new_my_remote_client_or_server (NULL,
+ g_my_ds, (etch_value_factory*) g_my_vf);
+ etch_remote* remote = myremote->remote_base;
+
+ /* this call goes through delivery service.transport_control,
+ * which we overrode to myds_transport_control() in our ds constuctor
+ * above. so we're not testing an actual connection waitup, we're testing
+ * that the call reaches delivery service.transport_control(), and that
+ * we can override delivery service.transport_control() at will.
+ */
+
+ result = remote->start_waitup(remote, ETCH_NOWAIT);
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(TRANSPORT_CONTROL, gds_what);
+ CU_ASSERT_EQUAL(gds_controlval, ETCH_NOWAIT);
+
+ result = remote->start_waitup(remote, THISWAITMS);
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(gds_controlval, THISWAITMS);
+
+ etch_object_destroy(myremote);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_stop_waitdown()
+ */
+static void test_stop_waitdown(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, resultval = 0, THISWAITMS = 3000;
+
+ my_remoteobj* myremote = new_my_remote_client_or_server (NULL,
+ g_my_ds, (etch_value_factory*) g_my_vf);
+ etch_remote* remote = myremote->remote_base;
+
+ /* this call goes through delivery service.transport_control,
+ * which we overrode to myds_transport_control() in our ds constuctor
+ * above. so we're not testing an actual connection waitdown, we're testing
+ * that the call reaches delivery service.transport_control(), and that
+ * we can override delivery service.transport_control() at will.
+ */
+
+ result = remote->stop_waitdown(remote, ETCH_NOWAIT);
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(TRANSPORT_CONTROL, gds_what);
+ CU_ASSERT_EQUAL(gds_controlval, ETCH_NOWAIT);
+
+ result = remote->stop_waitdown(remote, THISWAITMS);
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(gds_controlval, THISWAITMS);
+
+ etch_object_destroy(myremote);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+#endif
+
+CU_pSuite test_etch_remote_suite()
+{
+ CU_pSuite ps = CU_add_suite("remote base test suite", init_suite, clean_suite);
+
+ // THESE TESTS ARE BROKEN
+ // TODO first fix the stub tests per java tests, then redo these tests similarly.
+ //CU_add_test(ps, "test test setup and teardown", test_setup);
+ //CU_add_test(ps, "test remote.new_message", test_new_message);
+ //CU_add_test(ps, "test remote.send", test_send);
+ //CU_add_test(ps, "test remote.begin_call", test_begincall);
+ //CU_add_test(ps, "test remote.end_call", test_endcall);
+ //CU_add_test(ps, "test remote.transport_query", test_transport_query);
+ //CU_add_test(ps, "test remote.transport_control", test_transport_control);
+ //CU_add_test(ps, "test remote.transport_notify", test_transport_notify);
+ //CU_add_test(ps, "test remote.start_waitup", test_start_waitup);
+ //CU_add_test(ps, "test remote.stop_waitdown", test_stop_waitdown);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/support/test_stub.c b/binding-c/runtime/c/src/test/support/test_stub.c
new file mode 100644
index 0000000..e24ffa7
--- /dev/null
+++ b/binding-c/runtime/c/src/test/support/test_stub.c
@@ -0,0 +1,1358 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_stub.c
+ * test stub, delivery service, etc.
+ */
+
+
+#include "etch_runtime.h"
+#include "etch_svcobj_masks.h"
+#include "etch_transport.h"
+#include "etch_thread.h"
+#include "etch_object.h"
+#include "etch_stub.h"
+#include "etch_default_value_factory.h"
+#include "etch_plain_mailbox_manager.h"
+#include "etch_transport.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_map.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+#ifdef _WIN32
+#pragma message ( "this testsuite is not active" )
+#else
+#warning "this testsuite is not active"
+#endif
+
+#if 0
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+#define THISTEST_WHO_VALUE 0x5151
+
+static unsigned short CLASSID_MY_VF;
+static unsigned short CLASSID_MY_VF_VTAB;
+static unsigned short CLASSID_MY_VF_IMPL;
+static unsigned short CLASSID_MY_IMPLOBJ;
+static unsigned short CLASSID_MY_IMPLBASE;
+
+static default_value_factory* new_fake_valuefactory();
+static etch_server_factory* new_my_stubparams(xxxx_either_impl*, etch_threadpool* qp, etch_threadpool* fp);
+
+static i_delivery_service* new_my_delivery_service();
+static etch_thread* thistest_proxy_threadpool_run (etch_threadpool*, etch_threadproc, void*);
+static char* LOGSRC = "TEST";
+
+typedef enum whats
+{ TRANSPORT_MESSAGE = 1, TRANSPORT_QUERY, TRANSPORT_CONTROL, TRANSPORT_NOTIFY,
+ SESSION_QUERY, SESSION_CONTROL, SESSION_NOTIFY, HOWDY,
+} whats;
+
+static etch_resources* g_my_resources;
+static default_value_factory* g_my_vf;
+static i_delivery_service* g_my_ds;
+static i_delivery_service* g_my_transport; /* alias for g_my_ds */
+static etch_tcp_delivery_service* g_my_dsimpl;
+static i_xxxx_either* g_my_iserver;
+static etch_threadpool* g_qpool;
+static etch_threadpool* g_fpool;
+static etch_who* g_who;
+static etch_type* g_mt_howdy;
+static etch_type* g_mt_nogood;
+static etch_server_factory* g_stubparams;
+static int g_is_run_on_queued_pool;
+static int g_is_run_on_free_pool;
+static int g_stub_errors;
+static etch_run g_real_fpool_run; /* saved threadpool run procs */
+static etch_run g_real_qpool_run;
+
+static etch_who* gds_who;
+static etch_message* gds_message;
+static etch_int32* gds_query_result;
+static int gds_what;
+static int gds_eventval;
+static int gds_queryval;
+static int gds_controlval;
+static int gds_valueval;
+
+static etch_who* gsv_who;
+static etch_message* gsv_message;
+static etch_int32* gsv_query_result;
+static i_delivery_service* gsv_ds;
+static int gsv_what;
+static int gsv_eventval;
+static int gsv_queryval;
+static int gsv_controlval;
+static int gsv_valueval;
+
+
+/**
+ * new_my_resources()
+ * resources map constructor
+ */
+static etch_resources* new_my_resources(void* valuefactory)
+{
+ etch_resources* resx = get_etch_transport_resources(NULL);
+ etch_resources_add(resx, ETCH_RESXKEY_MSGIZER_VALUFACT, valuefactory);
+
+ g_qpool = (etch_threadpool*) etch_resources_get (resx, ETCH_RESXKEY_POOLTYPE_QUEUED);
+ g_real_qpool_run = g_qpool->run; /* save the real run procedure */
+ g_qpool->run = thistest_proxy_threadpool_run; /* intercept the queued pool run */
+
+ g_fpool = (etch_threadpool*) etch_resources_get (resx, ETCH_RESXKEY_POOLTYPE_FREE);
+ g_real_fpool_run = g_fpool->run; /* save the real run procedure */
+ g_fpool->run = thistest_proxy_threadpool_run; /* intercept the free pool run */
+
+ return resx;
+}
+
+
+/**
+ * setup_this_test()
+ * set up an individual unit test
+ */
+static int setup_this_test()
+{
+ CLASSID_MY_VF = get_dynamic_classid();
+ CLASSID_MY_VF_VTAB = get_dynamic_classid();
+ CLASSID_MY_VF_IMPL = get_dynamic_classid();
+ #if(IS_DEBUG_CONSOLE)
+ printf("\n");
+ #endif
+
+ g_my_vf = new_fake_valuefactory();
+ set_etchobj_static_all(g_my_vf); /* so resources map will not destroy */
+
+ /* get resources map populated with transport resources such as thread pools */
+ g_my_resources = new_my_resources(g_my_vf);
+
+ //g_my_ds = new_my_delivery_service ();
+ //g_my_transport = g_my_ds; /* alias */
+
+ g_who = new_who(new_int32(THISTEST_WHO_VALUE));
+ gds_query_result = new_int32(1);
+ gsv_query_result = new_int32(2);
+
+ //g_my_dsimpl = g_my_ds->thisx;
+ //assert(is_etch_deliverysvc(g_my_dsimpl));
+ //assert(is_etch_sessionmsg(g_my_dsimpl->sessionmsg));
+
+ return 0;
+}
+
+
+/**
+ * setup_this_stub()
+ * setup for stub
+ */
+static int setup_this_stub(xxxx_either_impl* implobj, unsigned char stubtype,
+ etch_threadpool* qp, etch_threadpool* fp)
+{
+ new_my_stubparams(implobj, qp, fp);
+
+ g_my_ds = new_my_delivery_service ();
+ g_my_transport = g_my_ds; /* alias */
+
+ g_my_dsimpl = g_my_ds->thisx;
+ assert(is_etch_deliverysvc(g_my_dsimpl));
+ assert(is_etch_sessionmsg(g_my_dsimpl->sessionmsg));
+
+ return 0;
+}
+
+
+/**
+ * teardown_this_test()
+ * tear down an individual unit test
+ */
+static void teardown_this_test()
+{
+ if (g_my_resources)
+ { /* we did a set_etchobj_static_all() on the g_my_vf value factory
+ * and as a result the map will not destroy it. if we had not done
+ * so, the vf would have been destroyed with the resources map. */
+ etch_object_destroy(g_my_resources);
+ }
+
+ if (g_my_vf)
+ { /* we clear the set_etchobj_static_all() on the g_my_vf value factory
+ * and as a result we can then destroy it */
+ clear_etchobj_static_all(g_my_vf);
+ etch_object_destroy(g_my_vf);
+ }
+
+ etch_object_destroy(g_my_ds);
+
+ etch_free(g_stubparams);
+ etch_free(g_my_iserver);
+
+ etch_object_destroy(g_who);
+ etch_object_destroy(gds_message);
+ etch_object_destroy(gds_query_result);
+
+ gds_what = 0;
+ gds_who = NULL;
+ g_my_vf = NULL;
+ g_my_ds = NULL;
+ g_who = NULL;
+ g_is_run_on_queued_pool = g_is_run_on_free_pool = g_stub_errors = 0;
+ g_mt_howdy = g_mt_nogood = NULL;
+ gds_message = NULL;
+ gds_eventval= gds_queryval = gds_controlval = gds_valueval = 0;
+ gds_query_result = NULL;
+ g_my_resources = NULL;
+
+ etch_object_destroy(gsv_message);
+ etch_object_destroy(gsv_query_result);
+
+ gsv_what = 0;
+ gsv_who = NULL;
+ gsv_ds = NULL;
+ gsv_message = NULL;
+ gsv_eventval= gsv_queryval = gsv_controlval = gsv_valueval = 0;
+ g_my_transport = NULL;
+ gsv_query_result = NULL;
+
+ etchvf_free_builtins();
+}
+
+
+/* - - - - - - - - - -
+ * stub implementor
+ * - - - - - - - - - -
+ */
+
+/**
+ * my_implob
+ * xxxx_either_impl with an extra method.
+ * this represents for this test what would be an impl_server or impl_client in practice.
+ */
+typedef struct my_implobj
+{
+ etch_object object;
+
+ i_xxxx_either* either_base; /* owned */
+ etch_object* ixxxx; /* not owned */
+ xxxx_remote_either* either; /* not owned */
+
+ int (*destroyex) (void*); /* user memory destructor */
+
+ /* - - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - - -
+ */
+ i_objsession* iobjsession; /* owned by base */
+ /* fyi: iobjsession->thisx is my_implobj* */
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - - - - - - - -
+ * custom instance data and methods
+ * - - - - - - - - - - - - - - - - -
+ */
+
+ int (*howdy) (struct my_implobj*, i_delivery_service*, etch_who*, etch_message*);
+
+} my_implobj;
+
+
+
+/* - - - - - - - - - -
+ * value factory
+ * - - - - - - - - - -
+ */
+
+/**
+ * my_valufactory_impl
+ * value factory instance data object
+ */
+typedef struct my_valufactory_impl
+{
+ etch_object object;
+
+ etch_type* mt_howdy;
+ etch_type* mt_nogood;
+
+} my_valufactory_impl;
+
+
+/**
+ * destroy_my_valufactory_impl()
+ * destructor for inheriting value factory instance data
+ */
+static int destroy_my_valufactory_impl(void* data)
+{
+ my_valufactory_impl* impl = (my_valufactory_impl*)data;
+ if (NULL == impl) return -1;
+
+ if (!is_etchobj_static_content(impl))
+ {
+ destroy_static_type(impl->mt_howdy);
+ destroy_static_type(impl->mt_nogood);
+ }
+
+ return destroy_objectex((etch_object*) impl);
+}
+
+
+/**
+ * new_my_valufactory_impl()
+ * constructor for our value factory's instance data
+ */
+static my_valufactory_impl* new_my_valufactory_impl()
+{
+ unsigned short class_id = CLASSID_MY_VF_IMPL? CLASSID_MY_VF_IMPL:
+ (CLASSID_MY_VF_IMPL = get_dynamic_classid());
+
+ my_valufactory_impl* impl = (my_valufactory_impl*) new_object
+ (sizeof(my_valufactory_impl), ETCHTYPEB_VALUEFACTIMP, class_id);
+
+ ((etch_object*)impl)->destroy = destroy_my_valufactory_impl;
+
+ impl->mt_howdy = new_static_type(L"howdy");
+ impl->mt_nogood = new_static_type(L"nogood");
+ g_mt_howdy = impl->mt_howdy;
+ g_mt_nogood = impl->mt_nogood;
+
+ return impl;
+}
+
+
+/**
+ * new_fake_valuefactory()
+ */
+static default_value_factory* new_fake_valuefactory()
+{
+ my_valufactory_impl* impl = NULL;
+ etchparentinfo* inheritlist = NULL;
+ const unsigned short classid_vf = get_dynamic_classid_unique(&CLASSID_MY_VF);
+ const unsigned short classid_vf_vtab = get_dynamic_classid_unique(&CLASSID_MY_VF_VTAB);
+
+ g_my_vf = new_default_value_factory(NULL, NULL);
+
+ /* ensure parent type keys exist in the (one-based) inheritance list.
+ * parent class of our custom vf is default_value_factory.
+ * inheritance list is used by validators and object assignment logic.
+ */
+ inheritlist = get_vtab_inheritance_list((etch_object*)g_my_vf, 2, 1, classid_vf_vtab);
+ inheritlist[1].obj_type = ETCHTYPEB_VALUEFACTORY;
+ inheritlist[1].class_id = CLASSID_VALUEFACTORY; /* parent class */
+ ((etch_object*)g_my_vf)->class_id = classid_vf; /* our class */
+
+ /* instantiate the custom vf's instance data and assign it to the vf.
+ * the impl comprises all data specific to the inheriting class, including
+ * data and methods if any. the default value factory destructor will call
+ * the destructor on the vf's impl object.
+ */
+ impl = new_my_valufactory_impl();
+ g_my_vf->impl = (etch_object*) impl;
+ ((struct i_value_factory*)((etch_object*)g_my_vf)->vtab)->add_type(g_my_vf, impl->mt_howdy);
+ ((struct i_value_factory*)((etch_object*)g_my_vf)->vtab)->add_type(g_my_vf, impl->mt_nogood);
+
+ return g_my_vf;
+}
+
+
+/* - - - - - - - - - -
+ * delivery service
+ * - - - - - - - - - -
+ */
+
+/**
+ * myds_begincall()
+ * typedef int (*etch_delivsvc_begincall)(void* thisx, etch_message*, void** out);
+ * @param msg caller relinquishes on success, retains on failure
+ * @param out mailbox interface returned on success
+ * @return 0 success, or -1 failure. new mailbox return in out parameter.
+ */
+static int myds_begincall (void *data, etch_message* msg, void** out)
+{
+ assert(out);
+ *out = NULL;
+ return -1;
+}
+
+
+/**
+ * myds_endcall()
+ * typedef int (*etch_delvisvc_endcall)(void* thisx, i_mailbox*, etch_type*, void** out);
+ * message response received. close mailbox and return response.
+ * @param mbox the current mailbox (interface), caller retains.
+ * @param response_type type of the response message, caller retains.
+ * @param out pointer to caller's location to receive the message response object.
+ * @return 0 success, -1 failure. response object returned via out parameter.
+ * @remarks assumed that the reply message and its wrapper are destroyed with the mailbox.
+ */
+static int myds_endcall (void* data, i_mailbox* mbox, etch_type* rtype, void** out)
+{
+ assert(out);
+ *out = NULL;
+ return -1;
+}
+
+/* - - - - - - - - - - - - - - - - -
+ * delivery service i_sessionmessage
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/* this is the delivery service interface implementation of i_sessionmessage,
+ * distinct from the transport.session's implementation of i_sessionmessage
+ * which is implemented externally and set via set_session().
+ */
+
+/**
+ * myds_session_message()
+ * @param whofrom caller retains, can be null.
+ * @param msg caller relinquishes
+ * @return 0 (message handled), or -1 (error, closed, or timeout)
+ */
+static int myds_session_message (void* data, etch_who* whofrom, etch_message* msg)
+{
+ etch_object_destroy(msg);
+ msg = NULL;
+
+ return 0;
+}
+
+
+/**
+ * myds_session_control()
+ * delivery service interface implementation of i_session_message
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ */
+static int myds_session_control (void* data, etch_event* control, etch_object* value)
+{
+ etch_object_destroy(control);
+ control = NULL;
+
+ etch_object_destroy(value);
+ value = NULL;
+
+ return 0;
+}
+
+
+/**
+ * myds_session_notify()
+ * @param evt event, caller relinquishes.
+ */
+static int myds_session_notify (void* data, etch_event* evt)
+{
+ etch_object_destroy(evt);
+ evt = NULL;
+
+ return 0;
+}
+
+
+/**
+ * myds_session_query()
+ * @param query, caller relinquishes.
+ */
+static etch_object* myds_session_query(void* data, etch_query* query)
+{
+ etch_object_destroy(query);
+ query = NULL;
+
+ return NULL;
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * delivery service i_transportmessage
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * myds_transport_message()
+ * @param whoto recipient - caller retains
+ * @param message caller relinquishes on success, retains on failure.
+ * @return 0 success, -1 error.
+ */
+static int myds_transport_message(void* data, void* whoData, void* messageData)
+{
+ gds_what = TRANSPORT_MESSAGE;
+ gds_who = (etch_who*)whoData;
+ etch_object_destroy((etch_message*)messageData);
+
+ return 0;
+}
+
+
+/**
+ * myds_transport_control()
+ * @param control caller relinquishes.
+ * @param value caller relinquishes.
+ */
+static int myds_transport_control (void* data, etch_event* control, etch_object* valueData)
+{
+ etch_int32* value = (etch_int32*)valueData;
+ gds_what = TRANSPORT_CONTROL;
+ gds_controlval = control->value;
+ gds_valueval = value->value;
+ return 0;
+}
+
+
+/**
+ * myds_transport_notify()
+ * i_transportmessage::transport_notify override.
+ * @param evt, caller relinquishes.
+ */
+static int myds_transport_notify (void* data, etch_event* evt)
+{
+ gds_what = TRANSPORT_NOTIFY;
+ gds_eventval = evt->value;
+ return 0;
+}
+
+
+/**
+ * myds_transport_query()
+ * i_transportmessage::transport_query override.
+ * @param query, caller relinquishes.
+ */
+static etch_object* myds_transport_query (void* data, etch_query* query)
+{
+ gds_what = TRANSPORT_QUERY;
+ gds_queryval = query->value;
+ return 0;
+}
+
+/**
+ * myds_get_session()
+ * i_transportmessage::get_session override.
+ */
+static i_session* myds_get_session (void* data)
+{
+ etch_tcp_delivery_service* thisx = (etch_tcp_delivery_service*)data;
+ return (i_session*)thisx->session;
+}
+
+
+/**
+ * myds_set_session()
+ * i_transportmessage::set_session override.
+ */
+static void myds_set_session (void* data, void* param)
+{
+ i_delivery_service* ids = (i_delivery_service*)data;
+ i_sessionmessage* newsession = (i_sessionmessage*)param;
+ /* we override methods in the ids itm. the itm object belongs to the ds transport,
+ * which is the mailbox manager. we need to ensure in the real world that the ds
+ * set_session() takes care of housekeeping similarly to this override.
+ */
+ etch_tcp_delivery_service* tcpds = get_etch_ds_impl(ids);
+ assert(is_etch_sessionmsg(newsession));
+ assert(tcpds->session == ids->session);
+ if (tcpds->session)
+ { assert(is_etch_sessionmsg(tcpds->session));
+ etch_object_destroy(tcpds->session);
+ }
+
+ /* replace delivery service impl's sessionmsng with stub's sesssionmsg */
+ tcpds->session = ids->session = newsession;
+ assert(is_etch_sessionmsg(tcpds->session));
+
+ /* we say that the ds owns the session, since even though the session belongs
+ * to the stub, the ds is expected to destroy the stub, and thus the session
+ * interface along with it.
+ */
+ ids->is_session_owned = TRUE;
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * delivery service construction
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_my_delivery_service()
+ */
+static i_delivery_service* new_my_delivery_service()
+{
+ etch_tcp_connection* nullconnection = NULL;
+ etch_tcp_delivery_service* delsvc = NULL;
+ i_sessionmessage* ism = NULL;
+ i_transportmessage* itm = NULL;
+
+ i_delivery_service* ids = new_etch_transport(L"http://www.cisco.com:9999/cuae",
+ (etch_factory_params*) g_stubparams, nullconnection);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(ids);
+
+ delsvc = ids->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(delsvc);
+ CU_ASSERT_EQUAL_FATAL(is_etch_deliverysvc(delsvc), TRUE);
+
+ ism = ids->ism;
+ itm = ids->itm;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(ism);
+ CU_ASSERT_EQUAL_FATAL(is_etch_sessionmsg(ism), TRUE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(itm);
+ CU_ASSERT_EQUAL_FATAL(is_etch_transportmsg(itm), TRUE);
+
+ /* override delivery service i_sessionmessage to implementations herein */
+ ids->begin_call = delsvc->begin_call = myds_begincall;
+ ids->end_call = delsvc->end_call = myds_endcall;
+ ism->session_message = delsvc->session_message = myds_session_message;
+ ism->session_control = delsvc->session_control = myds_session_control;
+ ism->session_notify = delsvc->session_notify = myds_session_notify;
+ ism->session_query = delsvc->session_query = myds_session_query;
+
+ /* override delivery service i_transportmessage to implementations herein.
+ * note that we swap out the virtuals, but the not the itm object, which
+ * is the ds transport, which is the mailbox manager's itm. the mailbox
+ * manager owns it and will destroy it when destroyed during destruction
+ * of the delivery service.
+ */
+ itm->transport_message = delsvc->transport_message = myds_transport_message;
+ itm->transport_control = delsvc->transport_control = myds_transport_control;
+ itm->transport_notify = delsvc->transport_notify = myds_transport_notify;
+ itm->transport_query = delsvc->transport_query = myds_transport_query;
+ itm->get_session = delsvc->get_session = myds_get_session;
+ itm->set_session = delsvc->set_session = myds_set_session;
+
+ return ids;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - -
+ * stub's implementing object
+ * - - - - - - - - - - - - - - - - - - -
+ */
+
+/* a stub's object is in practice a server implementation, masked by xxxx_either_impl,
+ * such as perf_server_impl. the obj_type will be ETCHTYPEB_EXESERVERIMPL -
+ * which is the type get_session_callbacks_from() recognizes to identify the obj as
+ * implementing i_objsession. constructors assigning ETCHTYPEB_EXESERVERIMPL are
+ * new_perf_remote_server() and init_perf_server_impl().
+ * stub.obj becomes this object. the delivery service session is set to the stub.
+ */
+
+#if(0)
+
+CLIENT SIDE
+perf_helper.new_remote_server()
+ remote_server = new_perf_remote_server (NULL, deliverysvc, vf);
+ i_perf_client* myclient = p->client = p->new_client(remote_server);
+ perf_client_stub* client_stub = new_perf_client_stub (p);
+ newstub->stub_base = new_stub (implobj, stubtype, ids, qp, fp);
+ /* set ds session to be stub's i_sessionmessage */
+ ids->itm->set_session (ids->itm, stubbase->isession);
+ /* copy stub implementor's i_objsession to the stub */
+ stubbase->impl_callbacks = etchstub_get_session_callbacks_from (implobj);
+#endif
+
+
+/* - - - - - - - - - - -
+ * implobj i_objsession
+ * - - - - - - - - - - -
+ */
+
+/**
+ * mysv_session_control()
+ * remote server i_objsession._session_control()
+ * @param control event, caller relinquishes.
+ * @param value control value, caller relinquishes.
+ * assuming we return a zero result to etchstub_session_control(),
+ * etchstub_session_control() assumes the control and value objects have been
+ * assumed by this method, so we destroy them accordingly.
+ */
+static int mysv_session_control (void* data, etch_event* control, etch_object* param)
+{
+ etch_int32* value = (etch_int32*)param;
+ if (control)
+ { gsv_controlval = control->value;
+ etch_object_destroy(control);
+ }
+ if (value)
+ { gsv_valueval = value->value;
+ etch_object_destroy(value);
+ }
+ gsv_what = SESSION_CONTROL;
+ return 0;
+}
+
+
+/**
+ * mysv_session_notify()
+ * remote server i_objsession._session_notify()
+ * @param evt event, caller relinquishes.
+ * assuming we return a zero result to etchstub_session_notify(),
+ * etchstub_session_notify() assumes the event object has been
+ * assumed by this method, so we destroy it accordingly.
+ */
+static int mysv_session_notify (void* data, etch_event* evt)
+{
+ if (evt)
+ { gsv_eventval = evt->value;
+ etch_object_destroy(evt);
+ }
+ gsv_what = SESSION_NOTIFY;
+ return 0;
+}
+
+
+/**
+ * mysv_session_query()
+ * remote server i_objsession._session_query()
+ * @param query, caller relinquishes.
+ * assuming we return a non-null result object to etchstub_session_query(),
+ * etchstub_session_query() assumes the query object has been assumed by
+ * this method, so we destroy it accordingly.
+ */
+static etch_int32* mysv_session_query (void* data, etch_query* query)
+{
+ if (query)
+ { gsv_queryval = query->value;
+ etch_free(query);
+ }
+ gsv_what = SESSION_QUERY;
+ return gsv_query_result;
+}
+
+
+/* - - - - - - - - - - - - - - - - -
+ * implementing object constructor
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * mysv_howdy
+ * implementation of my_implobj.howdy().
+ */
+static int mysv_howdy (my_implobj* thisx, i_delivery_service* ids, etch_who* who, etch_message* msg)
+{
+ gsv_what = HOWDY;
+ gsv_ds = ids;
+ gsv_who = who;
+ gsv_message = msg;
+ return 0;
+}
+
+
+/**
+ * destroy_my_implobj()
+ * destructor for our fake stub impl object
+ */
+static int destroy_my_implobj(void* data)
+{
+ my_implobj* thisx = (my_implobj*)data;
+ etch_free(thisx->either_base->iobjsession);
+ etch_object_destroy(thisx->either_base);
+ thisx->either_base = NULL;
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * new_my_implobj()
+ * instatiate and return an implementing object for the test stub.
+ * the only requisite of the stub's implementing object vis a vis this test,
+ * is that it implement the i_objsession interface. the stub makes this
+ * determination in etchstub_get_session_callbacks_from().
+ */
+static my_implobj* new_my_implobj()
+{
+ i_objsession* ios = NULL;
+
+ /* for the test it doesn't matter whether we use obj_type of client or server,
+ * it needs to be one or the other so etchstub_get_session_callbacks_from()
+ * will be able to extract its objession interface. in pratice the implementing
+ * object would be either a client implementation or a server implementation,
+ * however the test uses only the session interfaces so the object is androgynous.
+ */
+ my_implobj* implobj = (my_implobj*) new_object (sizeof(my_implobj),
+ ETCHTYPEB_EXECLIENTIMPL, get_dynamic_classid_unique(&CLASSID_MY_IMPLOBJ));
+
+ implobj->either_base = (i_xxxx_either*) new_object (sizeof(i_xxxx_either),
+ ETCHTYPEB_EXECLIENTBASE, get_dynamic_classid_unique(&CLASSID_MY_IMPLBASE));
+
+ /* populate as much of i_xxxx_either as we need */
+ ((etch_object*)implobj)->destroy = destroy_my_implobj;
+ implobj->either_base->thisx = (etch_object*) implobj;
+ ios = new_default_objsession_interface (implobj);
+ implobj->either_base->iobjsession = ios;
+ implobj->either_base->_session_control = ios->_session_control = mysv_session_control;
+ implobj->either_base->_session_notify = ios->_session_notify = mysv_session_notify;
+ implobj->either_base->_session_query = ios->_session_query = mysv_session_query;
+
+ /* populate as much of xxxx_either_impl as we need */
+ implobj->iobjsession = ios;
+ implobj->_session_control = ios->_session_control;
+ implobj->_session_notify = ios->_session_notify;
+ implobj->_session_query = ios->_session_query;
+
+ implobj->howdy = mysv_howdy; /* custom method */
+ return implobj;
+}
+
+
+/* - - - - - - - - - - -
+ * thread pool override
+ * - - - - - - - - - - -
+ */
+
+/**
+ * thistest_proxy_run()
+ * intercept of thread pool's run() which susequently calls the real run()
+ */
+static etch_thread* thistest_proxy_threadpool_run (etch_threadpool* pool, etch_threadproc threadproc, void* threaddata)
+{
+ switch(pool->pooltype)
+ {
+ case ETCH_THREADPOOLTYPE_FREE:
+ g_is_run_on_free_pool = TRUE;
+ return g_real_fpool_run (pool, threadproc, threaddata);
+
+ case ETCH_THREADPOOLTYPE_QUEUED:
+ g_is_run_on_queued_pool = TRUE;
+ return g_real_qpool_run (pool, threadproc, threaddata);
+ }
+
+ return NULL;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - -
+ * stub
+ * - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * stubhelper_howdy()
+ * stub helper (execution logic) for the mt_howdy message type. such methods
+ * conform to typedef opaque_stubhelper and are defined with the stub, and
+ * attached to the type in the stub implementation constructor.
+ */
+static int mystub_run_howdy (void* stub, i_delivery_service* ds, i_xxxx_either* obj, etch_who* whofrom, etch_message* msg)
+{
+ int result = 0;
+ my_implobj* implobj = NULL;
+ if (NULL == obj || NULL == obj->thisx)
+ { /* we can't use cunit macros away from the main thread, so we log errors instead */
+ ETCH_LOG(LOGSRC, ETCH_LOG_ERROR, "stub helper object null pointer\n");
+ g_stub_errors++;
+ return -1;
+ }
+
+ implobj = (my_implobj*) obj->thisx;
+
+ if (((etch_object*)implobj)->class_id != CLASSID_MY_IMPLOBJ)
+ { ETCH_LOG(LOGSRC, ETCH_LOG_ERROR, "unexpected stub helper object type\n");
+ g_stub_errors++;
+ return -1;
+ }
+
+ /* execute implemented service method */
+ result = implobj->howdy (implobj, ds, whofrom, msg);
+
+ if (0 != result)
+ { ETCH_LOG(LOGSRC, ETCH_LOG_ERROR, "service method implementation failed\n");
+ g_stub_errors++;
+ return -1;
+ }
+
+ return result;
+}
+
+
+/**
+ * new_my_stubparams()
+ * the stub carries around an etch_server_factory parameter bundle, which
+ * contains not only the server "factories" (virtual constructors, not used
+ * in these tests), but also various other parameters needed by methods which
+ * see the stub, and which a real-world stub would not have access to globally.
+ * one such parameter is the "server", masked by i_xxxx_either, from which the
+ * stub helper exepcts to extract the stub implementation object. we therefore
+ * must instantiate one of these objects for the purposes of this test.
+ */
+static etch_server_factory* new_my_stubparams(xxxx_either_impl* implobj, etch_threadpool* qp, etch_threadpool* fp)
+{
+ g_stubparams = new_server_factory (NULL, NULL, NULL, NULL);
+ g_stubparams->fpool = fp;
+ g_stubparams->qpool = qp;
+ // g_stubparams->in_delsvc = g_my_ds;
+ g_stubparams->in_resx = g_my_resources;
+ g_stubparams->in_valufact = (etch_value_factory*) g_my_vf;
+ /* params->server is an i_xxxx_server, expected by the stub helper
+ * (mystub_run_howdy in this case) */
+ g_my_iserver = etch_malloc(sizeof(i_xxxx_either), 0);
+ memset(g_my_iserver, 0, sizeof(i_xxxx_either));
+ g_my_iserver->thisx = (etch_object*) implobj;
+ // g_stubparams->server = g_my_iserver;
+ return g_stubparams;
+}
+
+
+/**
+ * new_stub()
+ * etch_stub (stub base) constructor.
+ * since we are testing only the stub base, we don't have access to the value
+ * factory through it, and so will instead use our global value factory to access
+ * the howdy message type and then set its stub helper to the above.
+ */
+static etch_stub* new_mystub (xxxx_either_impl* implobj, unsigned char stubtype,
+ i_delivery_service* ids, etch_threadpool* qp, etch_threadpool* fp)
+// TODO REMOVE DS FROM THIS API
+{
+ etch_stub* stubbase = NULL;
+ xxxx_either_stub* stubimpl = NULL;
+ i_xxxx_either* stubimplobj = implobj->either_base;
+
+ setup_this_stub(implobj, stubtype, qp, fp);
+ assert(is_etch_sessionmsg(g_my_dsimpl->sessionmsg));
+
+ // stub base is instatiated in new_stubimpl_init()
+ // stubbase = new_stub (implobj, stubtype, g_my_ds, qp, fp);
+
+ stubimpl = new_stubimpl_init (stubimplobj, sizeof(xxxx_either_stub),
+ stubtype, NULL, g_my_ds, qp, fp, g_stubparams);
+
+ stubbase = stubimpl->stub_base;
+ stubbase->is_implobj_owned = TRUE; /* let stubbase destroy its implobj */
+
+ assert(is_etch_sessionmsg(g_my_dsimpl->sessionmsg));
+
+ /* fyi new_stub() has copied the objesession interface from implobj thusly:
+ * stubbase->impl_callbacks = etchstub_get_session_callbacks_from (implobj);
+ */
+
+ /* instantiate parameter bundle to provide stubbase helper arguments */
+ // stubbase->params = new_my_stubparams(implobj, qp, fp);
+ // stubbase->params->stubbase = stubbase;
+
+ /* these tests call the stubbase's session_message(), session_notify, etc.
+ * note that the stubbase's i_sessionmessage implementations, which are
+ * etchstub_session_message, etchstub_session_notify(), etc., invoke the
+ * objsession callbacks as set per comment immediately above.
+ * of particular note is etchstub_session_message, which gets the type
+ * from the message, gets the stubbase "helper" (run procedure) from the message
+ * type, checks the type's async mode, and runs the proc inline or on a thread
+ * accordingly.
+ */
+ etchtype_set_type_stubhelper (g_mt_howdy, mystub_run_howdy);
+ stubbase->session_message = myds_session_message;
+
+ return stubbase;
+}
+
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * unit tests
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_setup()
+ */
+static void test_setup(void)
+{
+ setup_this_test();
+
+ do
+ {
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_message_1()
+ * test a stub runner run on main thread
+ */
+static void test_session_message_1(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_message* newmsg = NULL;
+ my_implobj* my_session = new_my_implobj (); /* should we move this to setup? */
+
+ etch_stub* stub = new_mystub ((xxxx_either_impl*) my_session,
+ ETCH_STUBTYPE_CLIENT, g_my_ds, g_qpool, g_fpool);
+
+ etchtype_set_async_mode (g_mt_howdy, ETCH_ASYNCMODE_NONE);
+
+ newmsg = new_message (g_mt_howdy, ETCH_DEFSIZE, (etch_value_factory*) g_my_vf);
+
+ /* fyi the default stub->session_message is etchstub_session_message(),
+ * which we do not override. this method gets the type from the message,
+ * get's the stub helper proc from the type, checks the type's async mode,
+ * and runs the stub helper proc inline or on a thread accordingly.
+ */
+
+ /* todo in some places we pass session_message a delivery service,
+ * but here we pass the stub. wtf? find the discrepancy and fix it.
+ * ds can get the stub via its ism.thisx. stub can get ds via stub.params.
+ */
+ result = stub->session_message (stub, g_who, newmsg);
+
+ CU_ASSERT_EQUAL(result, 0); /* 0 indicates message handled */
+ CU_ASSERT_EQUAL(g_stub_errors, 0);
+ CU_ASSERT_EQUAL(HOWDY, gsv_what);
+ CU_ASSERT_EQUAL(g_my_transport, gsv_ds);
+ CU_ASSERT_EQUAL(g_who, gsv_who);
+ CU_ASSERT_EQUAL(newmsg, gsv_message);
+ CU_ASSERT_EQUAL(g_is_run_on_queued_pool, FALSE);
+ CU_ASSERT_EQUAL(g_is_run_on_free_pool, FALSE);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_message_2()
+ * test a stub runner run on queued thread pool
+ */
+static void test_session_message_2(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_message* newmsg = NULL;
+ my_implobj* my_session = new_my_implobj (); /* should we move this to setup? */
+
+ etch_stub* stub = new_mystub ((xxxx_either_impl*) my_session,
+ ETCH_STUBTYPE_CLIENT, g_my_ds, g_qpool, g_fpool);
+
+ etchtype_set_async_mode (g_mt_howdy, ETCH_ASYNCMODE_QUEUED);
+
+ newmsg = new_message (g_mt_howdy, ETCH_DEFSIZE, (etch_value_factory*) g_my_vf);
+
+ /* fyi the default stub->session_message is etchstub_session_message(),
+ * which we do not override. this method gets the type from the message,
+ * get's the stub helper proc from the type, checks the type's async mode,
+ * and runs the stub helper proc inline or on a thread accordingly.
+ */
+ result = stub->session_message (stub, g_who, newmsg);
+
+ CU_ASSERT_EQUAL(result, 0); /* 0 indicates message handled */
+ CU_ASSERT_EQUAL(g_stub_errors, 0);
+ CU_ASSERT_EQUAL(HOWDY, gsv_what);
+ CU_ASSERT_EQUAL(g_my_transport, gsv_ds);
+ CU_ASSERT_EQUAL(g_who, gsv_who);
+ CU_ASSERT_EQUAL(newmsg, gsv_message);
+
+ /* if we don't implement a queued pool, a free pool was substituted */
+ #if ETCH_HAS_QUEUED_THREADPOOL
+ CU_ASSERT_EQUAL(g_is_run_on_queued_pool, TRUE);
+ CU_ASSERT_EQUAL(g_is_run_on_free_pool, FALSE);
+ #else
+ CU_ASSERT_EQUAL(g_is_run_on_free_pool, TRUE);
+ #endif
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_message_3()
+ * test a stub runner run on free thread pool
+ */
+static void test_session_message_3(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_message* newmsg = NULL;
+ my_implobj* my_session = new_my_implobj (); /* should we move this to setup? */
+
+ etch_stub* stub = new_mystub ((xxxx_either_impl*) my_session,
+ ETCH_STUBTYPE_CLIENT, g_my_ds, g_qpool, g_fpool);
+
+ etchtype_set_async_mode (g_mt_howdy, ETCH_ASYNCMODE_FREE);
+
+ newmsg = new_message (g_mt_howdy, ETCH_DEFSIZE, (etch_value_factory*) g_my_vf);
+
+ /* fyi the default stub->session_message is etchstub_session_message(),
+ * which we do not override. this method gets the type from the message,
+ * get's the stub helper proc from the type, checks the type's async mode,
+ * and runs the stub helper proc inline or on a thread accordingly.
+ */
+ result = stub->session_message (stub, g_who, newmsg);
+
+ CU_ASSERT_EQUAL(result, 0); /* 0 indicates message handled */
+ CU_ASSERT_EQUAL(g_stub_errors, 0);
+ CU_ASSERT_EQUAL(HOWDY, gsv_what);
+ CU_ASSERT_EQUAL(g_my_transport, gsv_ds);
+ CU_ASSERT_EQUAL(g_who, gsv_who);
+ CU_ASSERT_EQUAL(newmsg, gsv_message);
+ CU_ASSERT_EQUAL(g_is_run_on_queued_pool, FALSE);
+ CU_ASSERT_EQUAL(g_is_run_on_free_pool, TRUE);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_message_4()
+ * test sending a message whose type has no stub runner
+ */
+static void test_session_message_4(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, was_msg_handled = 0;
+ etch_message* newmsg = NULL;
+ my_implobj* my_session = new_my_implobj (); /* should we move this to setup? */
+
+ etch_stub* stub = new_mystub ((xxxx_either_impl*) my_session,
+ ETCH_STUBTYPE_CLIENT, g_my_ds, g_qpool, g_fpool);
+
+ newmsg = new_message (g_mt_nogood, ETCH_DEFSIZE, (etch_value_factory*) g_my_vf);
+
+ /* fyi the default stub->session_message is etchstub_session_message(),
+ * which we do not override. this method gets the type from the message,
+ * get's the stub helper proc from the type, checks the type's async mode,
+ * and runs the stub helper proc inline or on a thread accordingly.
+ */
+ result = stub->session_message (stub, g_who, newmsg);
+
+ was_msg_handled = (0 == result);
+ CU_ASSERT_EQUAL(was_msg_handled, FALSE);
+ CU_ASSERT_EQUAL(gsv_what, 0);
+ CU_ASSERT_PTR_NULL(gsv_ds);
+ CU_ASSERT_PTR_NULL(gsv_who);
+ CU_ASSERT_PTR_NULL(gsv_message);
+
+ /* fyi messages are not relinquished on failure
+ * since caller may want to reroute the message on failure */
+ if (!was_msg_handled)
+ etch_object_destroy(newmsg);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_query()
+ */
+static void test_session_query(void)
+{
+ setup_this_test();
+
+ do
+ { const int THISQUERYVAL = 12345;
+ etch_object* resultobj = NULL;
+ my_implobj* my_session = new_my_implobj ();
+
+ etch_stub* stub = new_mystub ((xxxx_either_impl*) my_session,
+ ETCH_STUBTYPE_SERVER, g_my_ds, g_qpool, g_fpool);
+
+ resultobj = stub->session_query (stub, new_etch_query(0, THISQUERYVAL));
+
+ CU_ASSERT_PTR_NOT_NULL(resultobj);
+ CU_ASSERT_PTR_EQUAL(resultobj, gsv_query_result);
+ CU_ASSERT_EQUAL(SESSION_QUERY, gsv_what);
+ CU_ASSERT_EQUAL(THISQUERYVAL, gsv_queryval);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_control()
+ */
+static void test_session_control(void)
+{
+ setup_this_test();
+
+ do
+ { const int THISCONTROLVAL = 12345, THISVALUEVAL = 54321;
+ int result = 0;
+ my_implobj* my_session = new_my_implobj ();
+
+ etch_stub* stub = new_mystub ((xxxx_either_impl*) my_session,
+ ETCH_STUBTYPE_SERVER, g_my_ds, g_qpool, g_fpool);
+
+ result = stub->session_control (stub, new_etch_control(0, THISCONTROLVAL), new_int32(THISVALUEVAL));
+
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(SESSION_CONTROL, gsv_what);
+ CU_ASSERT_EQUAL(THISCONTROLVAL, gsv_controlval);
+ CU_ASSERT_EQUAL(THISVALUEVAL, gsv_valueval);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_notify()
+ */
+static void test_session_notify(void)
+{
+ setup_this_test();
+
+ do
+ { const int THISNOTIFYVAL = 12345;
+ int result = 0;
+ my_implobj* my_session = new_my_implobj ();
+
+ etch_stub* stub = new_mystub ((xxxx_either_impl*) my_session,
+ ETCH_STUBTYPE_SERVER, g_my_ds, g_qpool, g_fpool);
+
+ result = stub->session_notify (stub, new_etch_event (0, THISNOTIFYVAL));
+
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(SESSION_NOTIFY, gsv_what);
+ CU_ASSERT_EQUAL(THISNOTIFYVAL, gsv_eventval);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+#endif
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_stub_suite()
+{
+ CU_pSuite ps = CU_add_suite("stub base test suite", init_suite, clean_suite);
+
+ // THESE TESTS ARE BROKEN
+ // TODO go back to java test and reimplement these tests
+ //CU_add_test(ps, "test test setup and teardown", test_setup);
+ //CU_add_test(ps, "test send message main thread", test_session_message_1);
+ //CU_add_test(ps, "test send message queued pool", test_session_message_2);
+ //CU_add_test(ps, "test send message freepool", test_session_message_3);
+ //CU_add_test(ps, "test send message no good", test_session_message_4);
+ //CU_add_test(ps, "test session query", test_session_query);
+ //CU_add_test(ps, "test session control", test_session_control);
+ //CU_add_test(ps, "test session notify", test_session_notify);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/test.c b/binding-c/runtime/c/src/test/test.c
new file mode 100644
index 0000000..18b2b7a
--- /dev/null
+++ b/binding-c/runtime/c/src/test/test.c
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main() {
+ void* t = malloc(0);
+ printf("%x\n", t);
+ printf("%d\n", strlen(0));
+
+}
diff --git a/binding-c/runtime/c/src/test/test_all.c b/binding-c/runtime/c/src/test/test_all.c
new file mode 100644
index 0000000..fed3fea
--- /dev/null
+++ b/binding-c/runtime/c/src/test/test_all.c
@@ -0,0 +1,102 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "test_all.h"
+
+CU_pSuite test_etchinternal_single_linked_list();
+CU_pSuite test_arraylist_suite();
+CU_pSuite test_cache_suite();
+CU_pSuite test_etch_arrayvalue_suite();
+CU_pSuite test_etch_binarytdit_suite();
+CU_pSuite test_etch_config_suite();
+CU_pSuite test_etch_defvaluefact_suite();
+CU_pSuite test_etch_encoding_suite();
+CU_pSuite test_etch_field_suite();
+CU_pSuite test_etch_flexbuf_suite();
+CU_pSuite test_etch_hashing_suite();
+CU_pSuite test_etch_hashtable_suite();
+CU_pSuite test_etch_idname_suite();
+CU_pSuite test_etch_iterator_suite();
+CU_pSuite test_etch_log_suite();
+CU_pSuite test_etch_mailboxmgr_suite();
+CU_pSuite test_etch_message_suite();
+CU_pSuite test_etch_messagizer_suite();
+CU_pSuite test_etch_mutex_suite();
+CU_pSuite test_etch_packetizer_suite();
+CU_pSuite test_etch_plainmailbox_suite();
+CU_pSuite test_etch_queue();
+CU_pSuite test_etch_remote_suite();
+CU_pSuite test_etch_runtime_suite();
+CU_pSuite test_etch_structvalue_suite();
+CU_pSuite test_etch_stub_suite();
+CU_pSuite test_etch_suite();
+CU_pSuite test_etch_tcpconn_suite();
+CU_pSuite test_etch_thread_suite();
+CU_pSuite test_etch_threadpool_suite();
+CU_pSuite test_etch_transport_suite();
+CU_pSuite test_etch_type_suite();
+CU_pSuite test_etch_url_suite();
+CU_pSuite test_etch_validator_suite();
+CU_pSuite test_etch_wait_suite();
+CU_pSuite test_etchobject_suite();
+CU_pSuite test_etch_linked_list_suite();
+
+suite_creator etch_testlist[] = {
+ test_etch_tcpconn_suite,
+ test_etch_linked_list_suite,
+ test_etchinternal_single_linked_list,
+ test_arraylist_suite,
+ test_cache_suite,
+ test_etch_arrayvalue_suite,
+ test_etch_binarytdit_suite,
+ test_etch_config_suite,
+ test_etch_defvaluefact_suite,
+ test_etch_encoding_suite,
+ test_etch_field_suite,
+ test_etch_flexbuf_suite,
+ test_etch_hashing_suite,
+ test_etch_hashtable_suite,
+ test_etch_idname_suite,
+ test_etch_iterator_suite,
+ test_etch_log_suite,
+ test_etch_message_suite,
+ test_etch_messagizer_suite,
+ test_etch_mutex_suite,
+ test_etch_packetizer_suite,
+ test_etch_queue,
+ test_etch_remote_suite,
+ test_etch_runtime_suite,
+ test_etch_structvalue_suite,
+ test_etch_stub_suite,
+ test_etch_suite,
+ test_etch_thread_suite,
+ test_etch_threadpool_suite,
+ test_etch_transport_suite,
+ test_etch_type_suite,
+ test_etch_url_suite,
+ test_etch_validator_suite,
+ test_etchobject_suite,
+ test_etch_wait_suite,
+ test_etch_mailboxmgr_suite,
+ test_etch_plainmailbox_suite,
+ 0
+};
+
diff --git a/binding-c/runtime/c/src/test/test_all.h b/binding-c/runtime/c/src/test/test_all.h
new file mode 100644
index 0000000..39d0ab8
--- /dev/null
+++ b/binding-c/runtime/c/src/test/test_all.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#ifndef _TEST_ALL_H_
+#define _TEST_ALL_H_
+
+#include "CUnit.h"
+
+typedef CU_pSuite (*suite_creator)();
+
+extern suite_creator etch_testlist[];
+
+#endif
diff --git a/binding-c/runtime/c/src/test/transport/test_mailboxmgr.c b/binding-c/runtime/c/src/test/transport/test_mailboxmgr.c
new file mode 100644
index 0000000..e6a4af1
--- /dev/null
+++ b/binding-c/runtime/c/src/test/transport/test_mailboxmgr.c
@@ -0,0 +1,1770 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_mailboxmgr.c
+ */
+#include "etch_runtime.h"
+#include "etch_plain_mailbox.h"
+#include "etch_plain_mailbox_manager.h"
+#include "etch_transport_message.h"
+#include "etch_default_value_factory.h"
+#include "etch_map.h"
+#include "etch_log.h"
+#include "etch_exception.h"
+#include "etch_simpletimer.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_object.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - -
+ */
+
+
+typedef enum etch_what
+{ WHAT_NONE,
+ SESSION_MESSAGE, SESSION_QUERY, SESSION_CONTROL, SESSION_NOTIFY,
+ TRANSPORT_MESSAGE, TRANSPORT_QUERY, TRANSPORT_CONTROL, TRANSPORT_NOTIFY,
+} etch_what;
+
+
+static etch_plainmailboxmgr* g_manager;
+static i_sessionmessage* g_my_session;
+static i_transportmessage* g_my_transport;
+static etch_who* g_who1;
+static etch_type* g_type1;
+static etch_mutex* g_rwlock;
+static default_value_factory* g_vf;
+static vf_idname_map* g_typemap;
+static class_to_type_map* g_c2tmap;
+
+static int g_is_unregistered;
+static int g_mailbox_status;
+
+static unsigned short CLASSID_MY_IMPL_TP;
+static unsigned short CLASSID_MY_IMPL_SM;
+#define OBJTYPE_MY_IMPL_TM 0x5170
+#define OBJTYPE_MY_IMPL_SM 0x5171
+
+#define is_my_impl_tm(x) (x && ((etch_object*)x)->obj_type == OBJTYPE_MY_IMPL_TM)
+#define is_my_impl_sm(x) (x && ((etch_object*)x)->obj_type == OBJTYPE_MY_IMPL_SM)
+
+typedef struct my_impl_transportmessage my_impl_transportmessage;
+typedef struct my_impl_sessionmessage my_impl_sessionmessage;
+
+static my_impl_transportmessage* new_my_impl_transportmessage();
+static my_impl_sessionmessage* new_my_impl_sessionmessage();
+
+
+#if 0
+/**
+ * mymboxmgr_unregister()
+ * override for mailbox manager unregister
+ */
+static int mymboxmgr_unregister (i_mailbox_manager* imgr, i_mailbox* mbox)
+{
+ g_is_unregistered = TRUE;
+ return 0;
+}
+
+
+/**
+ * mymboxmgr_redeliver()
+ * override for mailbox manager redeliver
+ */
+static int mymboxmgr_redeliver (i_mailbox_manager* imgr, etch_who* whofrom, etch_message* msg)
+{
+ return 0;
+}
+
+
+/**
+ * my_mailbox_notify()
+ * override for mailbox notify
+ */
+static int my_mailbox_notify (etch_plainmailbox* mbox, i_mailbox* mb, etch_object* state, const int is_closed)
+{
+ g_mailbox = mb;
+ g_mailbox_state = state;
+ g_mailbox_status = TRUE;
+ g_mailbox_isclosed = is_closed;
+ return 0;
+}
+
+static int is_equal_who(etch_who* who1, etch_who* who2)
+{
+ int n1 = 0, n2 = 0;
+ if (!who1 || !who2) return FALSE;
+ if (((etch_object*)who1)->class_id != CLASSID_WHO || ((etch_object*)who2)->class_id != CLASSID_WHO) return FALSE;
+ if (!who1->value || !who2->value) return FALSE;
+ if (!is_etch_int32(who1->value) || !is_etch_int32(who2->value)) return FALSE;
+ n1 = ((etch_int32*)who1->value)->value;
+ n2 = ((etch_int32*)who2->value)->value;
+ return n1 == n2;
+}
+#endif
+
+
+/**
+ * new_add_message
+ * convenience method to create a message of type "add"
+ */
+static etch_message* new_add_message()
+{
+ /* this call gets the "add" type from the value factory's types map.
+ * if the type is not present, one is created and added to the map. the vf's
+ * types map contains all builtin types, plus user types such as this. when
+ * the vf is destroyed, its types map is destroyed (if we let the vf create
+ * its own map at construction time rather than supplying a map to the vf
+ * constructor). when a types map is destroyed, it "destroys" its types.
+ * however builtin types are marked static and so the type destructor will
+ * take no action. user types are not so marked and so will be destroyed
+ * at that time.
+ */
+ etch_type* mt_add = etchtypemap_get_by_name(g_vf->types, L"add");
+ etch_message* newmsg = new_message(mt_add, ETCH_DEFSIZE, (etch_value_factory*) g_vf);
+ etchtype_put_validator(mt_add, clone_field(builtins._mf__message_id), (etch_object*) etchvtor_int64_get(0));
+ return newmsg;
+}
+
+
+/**
+ * get_add_result_type
+ */
+static etch_type* get_add_result_type()
+{
+ etch_type* mt_add_result = etchtypemap_get_by_name(g_vf->types, L"add_result");
+ etchtype_put_validator(mt_add_result, clone_field(builtins._mf__message_id), (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(mt_add_result, clone_field(builtins._mf__in_reply_to),(etch_object*) etchvtor_int64_get(0));
+ return mt_add_result;
+}
+
+
+/**
+ * new_add_result_message
+ * convenience method to create a message of type "add_result"
+ */
+static etch_message* new_add_result_message()
+{
+ /* this call gets the "add_result" type from the value factory's types map.
+ * if the type is not present, one is created and added to the map. the vf's
+ * types map contains all builtin types, plus user types such as this. when
+ * the vf is destroyed, its types map is destroyed (if we let the vf create
+ * its own map at construction time rather than supplying a map to the vf
+ * constructor). when a types map is destroyed, it "destroys" its types.
+ * however builtin types are marked static and so the type destructor will
+ * take no action. user types are not so marked and so will be destroyed
+ * at that time.
+ */
+ etch_type* mt_add_result = get_add_result_type();
+ etch_message* newmsg = new_message(mt_add_result, ETCH_DEFSIZE, (etch_value_factory*) g_vf);
+ return newmsg;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * my_impl_transportmessage (i_transportmessage implementation)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * my_impl_transportmessage
+ * test object implementing i_transportmessage
+ */
+//typedef struct my_impl_transportmessage
+struct my_impl_transportmessage
+{
+ unsigned int hashkey;
+ unsigned short obj_type;
+ unsigned short class_id;
+ struct etch_object* vtab;
+ int (*destroy)(void*);
+ void*(*clone) (void*);
+ obj_gethashkey get_hashkey;
+ struct etch_object* parent;
+ etchresult* result;
+ unsigned int refcount;
+ unsigned int length;
+ unsigned char is_null;
+ unsigned char is_copy;
+ unsigned char is_static;
+ unsigned char reserved;
+
+ /* i_transportmessage interface and methods, plus original destructor
+ * which becomes replaced with a custom destructor to destroy this
+ * object. this is the model for destroying an interface wrapper object
+ * when we do not save and pass around a pointer to the wrapper, but rather
+ * a pointer to the interface. the interface in question, i_transportmessage
+ * in this case, contains a pointer to the wrapper object, in this case a
+ * my_impl_transportmessage*. when the interface is instantiated, its original
+ * destructor is saved, and is replaced with a destructor which invokes
+ * the wrapper's destructor. the wrapper destructor must then know to
+ * invoke the interface's original destructor when destroying the interface.
+ */
+ i_transportmessage* ixm; /* owned */
+ etch_object_destructor destroy_transportmessage; /* i_transportmessage original destructor */
+ etch_transport_message transport_message; /* transport_message() */
+
+ i_sessionmessage* session; /* not owned */
+
+ etch_who* recipient; /* not owned */
+ etch_message* msg; /* not owned */
+ etch_what what;
+ size_t bufcount;
+ char* buf; /* owned */
+ etch_object* query; /* owned */
+ etch_object* query_result; /* owned */
+ etch_object* control; /* owned */
+ etch_object* value; /* owned */
+ etch_object* eventx; /* owned */
+};
+//} my_impl_transportmessage;
+
+
+/**
+ * destroy_my_impl_transportmessage()
+ * my_impl_transportmessage destructor
+ */
+static int destroy_my_impl_transportmessage (void* data)
+{
+ my_impl_transportmessage* thisx = (my_impl_transportmessage*)data;
+ if (!is_etchobj_static_content(thisx))
+ { /* invoke original i_transportmessage destructor */
+ if (thisx->ixm && thisx->destroy_transportmessage)
+ thisx->destroy_transportmessage(thisx->ixm);
+
+ if (thisx->buf)
+ etch_free(thisx->buf);
+
+
+ etch_object_destroy(thisx->query);
+ etch_object_destroy(thisx->query_result);
+ etch_object_destroy(thisx->control);
+ etch_object_destroy(thisx->value);
+ etch_object_destroy(thisx->eventx);
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * impl_transport_message()
+ * my_impl_transportmessage::transport_message
+ * @param whoto caller retains, can be null
+ * @param fbuf caller retains
+ */
+static int impl_transport_message (void* data, void* who, void* messageData)
+{
+ my_impl_transportmessage* mytm = (my_impl_transportmessage*)data;
+ etch_who* whoto = (etch_who*)who;
+ etch_message* msg = (etch_message*)messageData;
+
+ CU_ASSERT_FATAL(is_my_impl_tm(mytm));
+ mytm->what = TRANSPORT_MESSAGE;
+ mytm->recipient = whoto;
+ mytm->msg = msg;
+ return 0;
+}
+
+
+/**
+ * my_transport_control()
+ * my_impl_transportmessage::itransport::transport_control
+ */
+static int my_transport_control (my_impl_transportmessage* mytm, etch_object* control, etch_object* value)
+{
+ CU_ASSERT_FATAL(is_my_impl_tm(mytm));
+ mytm->what = TRANSPORT_CONTROL;
+ mytm->control = control;
+ mytm->value = value;
+ return 0;
+}
+
+
+/**
+ * my_transport_notify()
+ * my_impl_transportmessage::itransport::transport_notify
+ */
+static int my_transport_notify (my_impl_transportmessage* mytm, etch_object* evt)
+{
+ CU_ASSERT_FATAL(is_my_impl_tm(mytm));
+ mytm->what = TRANSPORT_NOTIFY;
+ mytm->eventx = evt;
+ return 0;
+}
+
+
+/**
+ * my_transport_query()
+ * my_impl_transportmessage::itransport::transport_query
+ */
+static etch_object* my_transport_query (my_impl_transportmessage* mytm, etch_object* query)
+{
+ etch_object* resultobj = NULL;
+ CU_ASSERT_FATAL(is_my_impl_tm(mytm));
+ resultobj = mytm->query_result; /* set artificially in test */
+ mytm->what = TRANSPORT_QUERY;
+ mytm->query = query;
+ mytm->query_result = NULL;
+ return (etch_object*) resultobj; /* caller owns */
+}
+
+
+/**
+ * my_transport_get_session()
+ * my_impl_transportmessage::itransport::get_session
+ */
+static i_sessionmessage* my_transport_get_session(my_impl_transportmessage* mytm)
+{
+ CU_ASSERT_FATAL(is_my_impl_tm(mytm));
+ return mytm->session;
+}
+
+
+/**
+ * my_transport_set_session()
+ * my_impl_transportmessage::itransport::set_session
+ */
+static void my_transport_set_session (my_impl_transportmessage* mytm, i_sessionmessage* session)
+{
+ CU_ASSERT_FATAL(is_my_impl_tm(mytm));
+ mytm->session = session;
+}
+
+
+/*
+ * destroy_my_transportmessage()
+ * i_transportmessage destructor
+ * this destructor will destroy its parent (my_impl_transportmessage),
+ * which will in turn destroy this object.
+ */
+static int destroy_my_transportmessage (void* data)
+{
+ i_transportmessage* itm = (i_transportmessage*)data;
+ my_impl_transportmessage* mytp = NULL;
+ if (NULL == itm) return -1;
+
+ mytp = itm->thisx;
+
+ etch_object_destroy(mytp);
+
+ return 0;
+}
+
+
+/**
+ * new_my_impl_transportmessage()
+ * my_impl_transportmessage constructor
+ */
+static my_impl_transportmessage* new_my_impl_transportmessage()
+{
+ i_transportmessage* itp = NULL;
+ i_transport* itransport = NULL;
+ /* this is a model for dynamic class ID assigment */
+ unsigned short class_id = CLASSID_MY_IMPL_TP? CLASSID_MY_IMPL_TP: (CLASSID_MY_IMPL_TP = get_dynamic_classid());
+
+ my_impl_transportmessage* mytp = (my_impl_transportmessage*) new_object
+ (sizeof(my_impl_transportmessage), OBJTYPE_MY_IMPL_TM, class_id);
+
+ mytp->destroy = destroy_my_impl_transportmessage;
+
+ itransport = new_transport_interface_ex(mytp,
+ (etch_transport_control) my_transport_control,
+ (etch_transport_notify) my_transport_notify,
+ (etch_transport_query) my_transport_query,
+ (etch_transport_get_session) my_transport_get_session,
+ (etch_transport_set_session) my_transport_set_session);
+
+ itp = new_transportmsg_interface(mytp, impl_transport_message, itransport);
+
+ /* save off i_transportmessage destructor */
+ mytp->destroy_transportmessage = ((etch_object*)itp)->destroy;
+
+ /* replace i_transportmessage destructor with one which will destroy this object */
+ ((etch_object*)itp)->destroy = destroy_my_transportmessage;
+
+ /* g_my_transport is set to this interface */
+ mytp->ixm = itp;
+
+ return mytp;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * my_impl_sessionmessage (i_sessionmessage implementation)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * my_impl_sessionmessage
+ * test object implementing i_sessionmessage
+ */
+struct my_impl_sessionmessage
+{
+ etch_object object;
+
+ /*
+ * i_sessionmessage interface and methods, plus original destructor
+ * which becomes replaced with a custom destructor to destroy this
+ * object. this is the model for destroying an interface wrapper object
+ * when we do not save and pass around a pointer to the wrapper, but rather
+ * a pointer to the interface. the interface in question, i_sessionmessage
+ * in this case, contains a pointer to the wrapper object, in this case a
+ * my_impl_sessionmessage*. when the interface is instantiated, its original
+ * destructor is saved, and is replaced with a destructor which invokes
+ * the wrapper's destructor. the wrapper destructor must then know to
+ * invoke the interface's original destructor when destroying the interface.
+ */
+ i_sessionmessage* ism; /* owned */
+ etch_object_destructor destroy_sessionmessage; /* i_sessionmessage original destructor */
+ etch_session_message session_message; /* session_message() method */
+
+ etch_what what;
+ etch_who* sender; /* not owned */
+ etch_message* msg; /* not owned */
+ int is_msg_handled;
+ etch_object* query; /* owned */
+ etch_object* query_result; /* owned */
+ etch_object* control; /* owned */
+ etch_object* value; /* owned */
+ etch_object* eventx; /* owned */
+};
+//} my_impl_sessionmessage;
+
+
+
+/**
+ * destroy_my_impl_sessionmessage()
+ * my_impl_sessionmessage destructor
+ */
+static int destroy_my_impl_sessionmessage(void* data)
+{
+ my_impl_sessionmessage* thisx = (my_impl_sessionmessage*)data;
+ if (!is_etchobj_static_content(thisx))
+ { /* invoke original i_sessionmessage destructor */
+ if (thisx->ism && thisx->destroy_sessionmessage)
+ thisx->destroy_sessionmessage(thisx->ism);
+
+ /* these are objects which would be destroyed in the binding
+ * by the last method to touch them */
+ etch_object_destroy(thisx->msg);
+
+ etch_object_destroy(thisx->query);
+
+ etch_object_destroy(thisx->query_result);
+
+ etch_object_destroy(thisx->control);
+
+ etch_object_destroy(thisx->value);
+
+ etch_object_destroy(thisx->eventx);
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * impl_session_message()
+ * my_impl_sessionmessage::ism::session_message.
+ * @param whofrom caller retains, can be null.
+ * @param msg caller abandons
+ */
+static int impl_session_message (void* data, etch_who* whofrom, etch_message* msg)
+{
+ my_impl_sessionmessage* mysm = (my_impl_sessionmessage*)data;
+
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ mysm->what = SESSION_MESSAGE;
+
+ /* in this emulation we are the session consuming a message. if successful,
+ * (i.e., the message is handled), the binding will eventually destroy the
+ * message (the caller relinquishes message memory), and the who object.
+ * if not successful (message not handled) the caller retains message and
+ * who memory in order to forward the message and who somewhere else
+ * (as an unwanted message). so we model that here: if the message is not
+ * handled (a manual switch in these tests), we do not save references to
+ * the messaqe and who for cleanup, because the unwanted message, containing
+ * these objects, will be cleaned up instead.
+ */
+ if (mysm->is_msg_handled)
+ {
+ mysm->msg = msg;
+ mysm->sender = whofrom;
+ return 0;
+ }
+
+ mysm->msg = NULL;
+ mysm->sender = NULL;
+ return -1;
+}
+
+
+/**
+ * my_session_control()
+ * my_impl_sessionmessage::ism::isession::session_control
+ * control and value are always abandoned by caller so mysm must clean them up.
+ */
+static int my_session_control (my_impl_sessionmessage* mysm, etch_object* control, etch_object* value)
+{
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ mysm->what = SESSION_CONTROL;
+ mysm->control = control;
+ mysm->value = value;
+ return 0;
+}
+
+
+/**
+ * my_session_notify()
+ * my_impl_sessionmessage::ism::isession::session_notify
+ * evt is always abandoned by caller so mysm must clean it up.
+ */
+static int my_session_notify (my_impl_sessionmessage* mysm, etch_object* evt)
+{
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ mysm->what = SESSION_NOTIFY;
+ mysm->eventx = evt;
+ return 0;
+}
+
+
+/**
+ * my_session_query()
+ * my_impl_sessionmessage::ism::isession::session_query
+ * query is always abandoned by caller so mysm must clean it up.
+ */
+static etch_object* my_session_query (my_impl_sessionmessage* mysm, etch_object* query)
+{
+ etch_object* resultobj = NULL;
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ resultobj = mysm->query_result; /* artifically set in test */
+ mysm->what = SESSION_QUERY;
+ mysm->query = query;
+ mysm->query_result = NULL;
+ return (etch_object*) resultobj; /* caller owns */
+}
+
+
+/*
+ * destroy_my_sessionmessage()
+ * i_sessionmessage destructor
+ * this destructor will destroy its parent (my_impl_sessionmessage),
+ * which will in turn destroy this object.
+ */
+static int destroy_my_sessionmessage(void* data)
+{
+ i_sessionmessage* ism = (i_sessionmessage*)data;
+ my_impl_sessionmessage* mysm = NULL;
+ if (NULL == ism) return -1;
+
+ mysm = ism->thisx;
+
+ etch_object_destroy(mysm);
+
+ return 0;
+}
+
+
+/**
+ * new_my_impl_sessionmessage()
+ * my_impl_sessionmessage constructor
+ */
+static my_impl_sessionmessage* new_my_impl_sessionmessage()
+{
+ i_sessionmessage* ism = NULL;
+ i_session* isession = NULL;
+ /* this is a model for dynamic class ID assigment */
+ unsigned short class_id = CLASSID_MY_IMPL_SM? CLASSID_MY_IMPL_SM:
+ (CLASSID_MY_IMPL_SM = get_dynamic_classid());
+
+ my_impl_sessionmessage* mysm = (my_impl_sessionmessage*) new_object
+ (sizeof(my_impl_sessionmessage), OBJTYPE_MY_IMPL_SM, class_id);
+
+ ((etch_object*)mysm)->destroy = destroy_my_impl_sessionmessage;
+
+ isession = new_session_interface(mysm,
+ (etch_session_control) my_session_control,
+ (etch_session_notify) my_session_notify,
+ (etch_session_query) my_session_query);
+
+ ism = new_sessionmsg_interface(mysm, impl_session_message, isession);
+
+ /* save off i_sessionmessage destructor */
+ mysm->destroy_sessionmessage = ((etch_object*)ism)->destroy;
+
+ /* custom destructor will destroy the my_impl_sessionmessage */
+ ((etch_object*)ism)->destroy = destroy_my_sessionmessage;
+
+ /* g_my_session will get set to this interface */
+ mysm->ism = ism;
+
+ return mysm;
+}
+
+
+/* - - - - - - - - - - - - - - -
+ * setup/teardown for each test
+ * - - - - - - - - - - - - - - -
+ */
+
+/**
+ * setup_this_test()
+ * set up an individual unit test
+ */
+static int setup_this_test()
+{
+ my_impl_transportmessage* mytm_impl = NULL;
+ my_impl_sessionmessage* mysm_impl = NULL;
+
+ g_who1 = new_who(new_int32(1));
+ g_type1 = new_type(L"type1");
+
+ g_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(g_typemap);
+ g_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(g_c2tmap);
+ defvf_initialize_static(g_typemap, g_c2tmap);
+ g_vf = new_default_value_factory(g_typemap, g_c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(g_vf);
+
+ // TODO: pool
+ etch_mutex_create(&g_rwlock, ETCH_MUTEX_UNNESTED, NULL);
+
+ etchtype_put_validator(g_type1, clone_field(builtins._mf__message_id), (etch_object*) etchvtor_int64_get(0));
+
+ /* we instantiate a wrapper x which implements and instantiates i_transportmessage.
+ * the instantiation of i_transportmessage will contain a pointer to x.
+ * our global reference g_my_transport is a pointer to the interface.
+ * the purpose of this excercise is that, in the real binding we can pass
+ * around the interface, whose methods can be then invoked without knowing
+ * anything about the wrapper. when we want to reference the wrapper x,
+ * it is (my_impl_transportmessage) g_my_transport->thisx.
+ */
+ mytm_impl = new_my_impl_transportmessage();
+ g_my_transport = mytm_impl->ixm;
+
+ mysm_impl = new_my_impl_sessionmessage();
+ g_my_session = mysm_impl->ism;
+
+ g_manager = new_plain_mailbox_manager (g_my_transport, NULL, NULL, g_rwlock);
+ g_manager->set_session (g_manager, g_my_session);
+
+ return g_manager? 0: -1;
+}
+
+
+/**
+ * teardown_this_test()
+ * tear down an individual unit test
+ */
+static void teardown_this_test()
+{
+ etch_object_destroy(g_who1);
+ g_who1 = NULL;
+
+ etch_object_destroy(g_type1);
+ g_type1 = NULL;
+
+ etch_object_destroy(g_vf);
+ g_vf = NULL;
+
+ etch_object_destroy(g_c2tmap);
+ g_c2tmap = NULL;
+
+ etch_object_destroy(g_typemap);
+ g_typemap = NULL;
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+ etch_object_destroy(g_manager);
+ g_manager = NULL;
+
+ etch_mutex_destroy(g_rwlock);
+ g_rwlock = NULL;
+
+ etch_object_destroy(g_my_transport);
+ g_my_transport = NULL;
+
+ etch_object_destroy(g_my_session);
+ g_my_session = NULL;
+
+ g_is_unregistered = g_mailbox_status = 0;
+ etchvf_free_builtins();
+}
+
+
+/* - - - - - - - - - - - - - -
+ * unit tests
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_test_setup()
+ * run test setup and teardown and verify all memory accounted for.
+ */
+static void test_test_setup(void)
+{
+ int result = setup_this_test();
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_constructor()
+ */
+static void test_constructor(void)
+{
+ setup_this_test();
+
+ do
+ { etch_object* session = 0;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_my_transport); /* g_my_transport->thisx = my_impl_transportmessage */
+
+ session = (etch_object*)g_my_transport->get_session (g_my_transport->thisx);
+ CU_ASSERT(is_etch_sessionmsg(session));
+ CU_ASSERT(is_etch_sessionmsg(g_manager->session));
+ /* used to work, no longer is the case, why are these no longer the same? */
+ // CU_ASSERT_PTR_EQUAL(g_manager->session, session);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_my_session);
+ CU_ASSERT_PTR_EQUAL(g_my_session, g_manager->get_session (g_manager));
+
+ CU_ASSERT_PTR_EQUAL(g_my_transport, g_manager->transport);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+
+/**
+ * test_session_query
+ */
+static void test_session_query(void)
+{
+ setup_this_test();
+
+ do
+ { const int MY_QUERY_CLASSID = 0x111, MY_RESULT_CLASSID = 0x112;
+ etch_object* myqueryobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_QUERY_CLASSID);
+ etch_object* myresultob = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_RESULT_CLASSID);
+ etch_object* query_result = NULL;
+ my_impl_sessionmessage* my_sessionimpl = g_my_session->thisx;
+ my_sessionimpl->query_result = myresultob;
+
+ query_result = g_manager->session_query(g_manager, (etch_query*)myqueryobj);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(query_result);
+ CU_ASSERT_EQUAL(my_sessionimpl->what, SESSION_QUERY);
+ CU_ASSERT_PTR_EQUAL(my_sessionimpl->query, myqueryobj);
+ CU_ASSERT_PTR_EQUAL(myresultob, query_result);
+ etch_object_destroy(myresultob);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_control
+ */
+static void test_session_control(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, MY_CONTROL_CLASSID = 0x111, MY_VALUE_CLASSID = 0x112;
+ etch_object* mycontrolobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_CONTROL_CLASSID);
+ etch_object* myvalueobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_VALUE_CLASSID);
+ my_impl_sessionmessage* my_sessionimpl = g_my_session->thisx;
+
+ result = g_manager->session_control(g_manager, (etch_event*)mycontrolobj, myvalueobj);
+
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(my_sessionimpl->what, SESSION_CONTROL);
+ CU_ASSERT_PTR_EQUAL(my_sessionimpl->control, mycontrolobj);
+ CU_ASSERT_PTR_EQUAL(my_sessionimpl->value, myvalueobj);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_notify
+ */
+static void test_session_notify(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, MY_EVENT_CLASSID = 0x111;
+ etch_object* myeventobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_EVENT_CLASSID);
+ my_impl_sessionmessage* my_sessionimpl = g_my_session->thisx;
+
+ result = g_manager->session_notify(g_manager, (etch_event*)myeventobj);
+
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(my_sessionimpl->what, SESSION_NOTIFY);
+ CU_ASSERT_PTR_EQUAL(my_sessionimpl->eventx, myeventobj);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_query
+ */
+static void test_transport_query(void)
+{
+ setup_this_test();
+
+ do
+ { const int MY_QUERY_CLASSID = 0x111, MY_RESULT_CLASSID = 0x112;
+ etch_object* myqueryobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_QUERY_CLASSID);
+ etch_object* myresultob = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_RESULT_CLASSID);
+ etch_object* query_result = NULL;
+ my_impl_transportmessage* my_transportimpl = g_my_transport->thisx;
+ my_transportimpl->query_result = myresultob;
+
+ query_result = g_manager->transport_query(g_manager, (etch_query*)myqueryobj);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(query_result);
+ CU_ASSERT_EQUAL(my_transportimpl->what, TRANSPORT_QUERY);
+ CU_ASSERT_PTR_EQUAL(my_transportimpl->query, myqueryobj);
+ CU_ASSERT_PTR_EQUAL(myresultob, query_result);
+ etch_object_destroy(myresultob);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_control
+ */
+static void test_transport_control(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, MY_CONTROL_CLASSID = 0x111, MY_VALUE_CLASSID = 0x112;
+ etch_object* mycontrolobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_CONTROL_CLASSID);
+ etch_object* myvalueobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_VALUE_CLASSID);
+ my_impl_transportmessage* my_transportimpl = g_my_transport->thisx;
+
+ result = g_manager->transport_control(g_manager, (etch_event*)mycontrolobj, myvalueobj);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(my_transportimpl->what, TRANSPORT_CONTROL);
+ CU_ASSERT_PTR_EQUAL(my_transportimpl->control, mycontrolobj);
+ CU_ASSERT_PTR_EQUAL(my_transportimpl->value, myvalueobj);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_notify
+ */
+static void test_transport_notify(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0, MY_EVENT_CLASSID = 0x111;
+ etch_object* myeventobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_EVENT_CLASSID);
+ my_impl_transportmessage* my_transportimpl = g_my_transport->thisx;
+
+ result = g_manager->transport_notify(g_manager, (etch_event*)myeventobj);
+ CU_ASSERT_EQUAL(result,0);
+ CU_ASSERT_EQUAL(my_transportimpl->what, TRANSPORT_NOTIFY);
+ CU_ASSERT_PTR_EQUAL(my_transportimpl->eventx, myeventobj);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_message_1()
+ */
+static void test_transport_message_1(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ i_mailbox* mailbox = NULL;
+ etch_message* addmsg = NULL;
+ etch_int64* addmsg_id = NULL, *addmsg_inreplyto = NULL;
+
+ my_impl_transportmessage* my_transport_impl = (my_impl_transportmessage*) g_my_transport->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport_impl);
+ CU_ASSERT_EQUAL(my_transport_impl->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport_impl->recipient);
+ CU_ASSERT_PTR_NULL(my_transport_impl->msg);
+
+ addmsg = new_add_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(addmsg);
+
+ addmsg_id = message_get_id(addmsg);
+ CU_ASSERT_PTR_NULL(addmsg_id);
+ addmsg_inreplyto = message_get_in_reply_to(addmsg);
+ CU_ASSERT_PTR_NULL(addmsg_inreplyto);
+
+ result = g_manager->transport_message(g_manager, g_who1, addmsg);
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(my_transport_impl->what, TRANSPORT_MESSAGE);
+ CU_ASSERT_EQUAL(my_transport_impl->recipient, g_who1);
+ CU_ASSERT_PTR_EQUAL(my_transport_impl->msg, addmsg);
+
+ addmsg_id = message_get_id(addmsg);
+ CU_ASSERT_PTR_NOT_NULL(addmsg_id);
+ addmsg_inreplyto = message_get_in_reply_to(addmsg);
+ CU_ASSERT_PTR_NULL(addmsg_inreplyto);
+
+ mailbox = pmboxmgr_get_mailbox(g_manager, addmsg_id);
+ CU_ASSERT_PTR_NULL(mailbox);
+
+ /* transport does not own the message so we destroy it here.
+ * a real world transport does not have state such as message, however
+ * if the transport is always the message terminal within this test suite
+ * we can destroy the message in the my_impl_transportmessage destructor,
+ * modeling the real world passing on of the message by the transport.
+ */
+ etch_object_destroy(addmsg);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_message_2()
+ */
+static void test_transport_message_2(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ const int THISTESTID = 1;
+ i_mailbox* mailbox = NULL;
+ etch_message* add_resultmsg = NULL;
+ etch_int64* add_resultmsg_id = NULL, *add_resultmsg_inreplyto = NULL;
+
+ my_impl_transportmessage* my_transport_impl = (my_impl_transportmessage*) g_my_transport->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport_impl);
+ CU_ASSERT_EQUAL(my_transport_impl->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport_impl->recipient);
+ CU_ASSERT_PTR_NULL(my_transport_impl->msg);
+
+ add_resultmsg = new_add_result_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(add_resultmsg);
+
+ add_resultmsg_id = message_get_id(add_resultmsg);
+ CU_ASSERT_PTR_NULL(add_resultmsg_id);
+ add_resultmsg_inreplyto = message_get_in_reply_to(add_resultmsg);
+ CU_ASSERT_PTR_NULL(add_resultmsg_inreplyto);
+
+ result = message_set_in_reply_to(add_resultmsg, new_int64(THISTESTID));
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = g_manager->transport_message(g_manager, g_who1, add_resultmsg);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(my_transport_impl->what, TRANSPORT_MESSAGE);
+ CU_ASSERT_EQUAL(my_transport_impl->recipient, g_who1);
+ CU_ASSERT_PTR_EQUAL(my_transport_impl->msg, add_resultmsg);
+
+ add_resultmsg_id = message_get_id(add_resultmsg);
+ CU_ASSERT_PTR_NOT_NULL(add_resultmsg_id);
+ add_resultmsg_inreplyto = message_get_in_reply_to(add_resultmsg);
+ CU_ASSERT_PTR_NOT_NULL(add_resultmsg_inreplyto);
+ if (add_resultmsg_inreplyto)
+ { CU_ASSERT_EQUAL(add_resultmsg_inreplyto->value, THISTESTID);
+ }
+
+ mailbox = pmboxmgr_get_mailbox(g_manager, add_resultmsg_id);
+ CU_ASSERT_PTR_NULL(mailbox);
+
+ /* transport does not own the message so we destroy it here.
+ * a real world transport does not have state such as message, however
+ * if the transport is always the message terminal within this test suite
+ * we can destroy the message in the my_impl_transportmessage destructor,
+ * modeling the real world passing on of the message by the transport.
+ */
+ etch_object_destroy(add_resultmsg);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_message_3()
+ * test attempt to send message that has already been sent
+ */
+static void test_transport_message_3(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ const int THISTESTID = 1;
+ etch_message* addmsg = NULL;
+ etch_int64* addmsg_id = NULL;
+
+ my_impl_transportmessage* my_transport_impl = (my_impl_transportmessage*) g_my_transport->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport_impl);
+ CU_ASSERT_EQUAL(my_transport_impl->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport_impl->recipient);
+ CU_ASSERT_PTR_NULL(my_transport_impl->msg);
+
+ addmsg = new_add_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(addmsg);
+
+ addmsg_id = message_get_id(addmsg);
+ CU_ASSERT_PTR_NULL(addmsg_id);
+ result = message_set_id(addmsg, new_int64(THISTESTID));
+ CU_ASSERT_EQUAL(result, 0);
+ addmsg_id = message_get_id(addmsg);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(addmsg_id);
+
+ result = g_manager->transport_message(g_manager, g_who1, addmsg);
+ CU_ASSERT_EQUAL(result, -1); /* should fail as already sent */
+
+ etch_object_destroy(addmsg);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_call_1()
+ * test sending a call message, then close mailbox for read
+ */
+static void test_transport_call_1(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_message* addmsg = NULL;
+ etch_plainmailbox* my_mbox_impl = NULL;
+ i_mailbox *mailbox = NULL, *got_mailbox = NULL;
+ etch_int64* addmsg_id = NULL, *addmsg_inreplyto = NULL;
+
+ my_impl_transportmessage* my_transport_impl = (my_impl_transportmessage*) g_my_transport->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport_impl);
+ CU_ASSERT_EQUAL(my_transport_impl->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport_impl->recipient);
+ CU_ASSERT_PTR_NULL(my_transport_impl->msg);
+
+ g_manager->session_notify (g_manager, new_etch_event(0, ETCHEVT_SESSION_UP));
+
+ addmsg = new_add_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(addmsg);
+
+ result = g_manager->transport_call (g_manager->imanager, g_who1,addmsg, &mailbox);
+
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mailbox);
+
+ CU_ASSERT_EQUAL(my_transport_impl->what, TRANSPORT_MESSAGE);
+ CU_ASSERT_EQUAL(my_transport_impl->recipient, g_who1);
+ CU_ASSERT_PTR_EQUAL(my_transport_impl->msg, addmsg);
+
+ result = pmboxmgr_size(g_manager);
+ CU_ASSERT_EQUAL(result, 1);
+
+ addmsg_id = message_get_id(addmsg);
+ CU_ASSERT_PTR_NOT_NULL(addmsg_id);
+ addmsg_inreplyto = message_get_in_reply_to(addmsg);
+ CU_ASSERT_PTR_NULL(addmsg_inreplyto);
+
+ got_mailbox = pmboxmgr_get_mailbox(g_manager, addmsg_id);
+ CU_ASSERT_PTR_NOT_NULL(got_mailbox);
+ CU_ASSERT_PTR_EQUAL(mailbox, got_mailbox);
+
+ mailbox->close_read (mailbox);
+
+ result = pmboxmgr_size(g_manager);
+ CU_ASSERT_EQUAL(result, 0);
+
+ got_mailbox = pmboxmgr_get_mailbox(g_manager, addmsg_id);
+ CU_ASSERT_PTR_NULL(got_mailbox);
+
+ /* in practice the last entity to handle the message would destroy it */
+ etch_object_destroy(addmsg);
+
+ /* it is not yet determined whether the mailbox manager should destroy an
+ * unregistered mailbox - for now it does not, so we destroy it now. */
+ my_mbox_impl = mailbox->thisx; /* get implementation from interface */
+ etch_object_destroy(my_mbox_impl);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_call_2()
+ * test sending a call message, then close mailbox for delivery
+ */
+static void test_transport_call_2(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_message* addmsg = NULL;
+ etch_plainmailbox* my_mbox_impl = NULL;
+ i_mailbox *mailbox = NULL, *got_mailbox = NULL;
+ etch_int64* addmsg_id = NULL, *addmsg_inreplyto = NULL;
+
+ my_impl_transportmessage* my_transport_impl = (my_impl_transportmessage*) g_my_transport->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport_impl);
+ CU_ASSERT_EQUAL(my_transport_impl->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport_impl->recipient);
+ CU_ASSERT_PTR_NULL(my_transport_impl->msg);
+
+ g_manager->session_notify(g_manager, new_etch_event(0, ETCHEVT_SESSION_UP));
+
+ addmsg = new_add_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(addmsg);
+
+ result = g_manager->transport_call(g_manager->imanager, g_who1,addmsg, &mailbox);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mailbox);
+
+ CU_ASSERT_EQUAL(my_transport_impl->what, TRANSPORT_MESSAGE);
+ CU_ASSERT_EQUAL(my_transport_impl->recipient, g_who1);
+ CU_ASSERT_PTR_EQUAL(my_transport_impl->msg, addmsg);
+
+ result = pmboxmgr_size(g_manager);
+ CU_ASSERT_EQUAL(result, 1);
+
+ addmsg_id = message_get_id(addmsg);
+ CU_ASSERT_PTR_NOT_NULL(addmsg_id);
+ addmsg_inreplyto = message_get_in_reply_to(addmsg);
+ CU_ASSERT_PTR_NULL(addmsg_inreplyto);
+
+ got_mailbox = pmboxmgr_get_mailbox(g_manager, addmsg_id);
+ CU_ASSERT_PTR_NOT_NULL(got_mailbox);
+ CU_ASSERT_PTR_EQUAL(mailbox, got_mailbox);
+
+ mailbox->close_delivery(mailbox);
+
+ result = pmboxmgr_size(g_manager);
+ CU_ASSERT_EQUAL(result, 0);
+
+ got_mailbox = pmboxmgr_get_mailbox(g_manager, addmsg_id);
+ CU_ASSERT_PTR_NULL(got_mailbox);
+
+ /* in practice the last entity to handle the message would destroy it */
+ etch_object_destroy(addmsg);
+
+ /* it is not yet determined whether the mailbox manager should destroy an
+ * unregistered mailbox - for now it does not, so we destroy it now. */
+ my_mbox_impl = mailbox->thisx; /* get implementation from interface */
+ etch_object_destroy(my_mbox_impl);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_call_3()
+ * test attempt to send a call message that has already been sent
+ */
+static void test_transport_call_3(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ i_mailbox* mailbox = NULL;
+ etch_int64* addmsg_id = NULL;
+
+ etch_message* addmsg = new_add_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(addmsg);
+ result = message_set_id(addmsg, new_int64(1)); /* set ID essentially marking message sent */
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = g_manager->transport_call(g_manager->imanager, g_who1, addmsg, &mailbox);
+ CU_ASSERT_NOT_EQUAL(result, 0); /* result should indicate error, already sent */
+
+ CU_ASSERT_PTR_NULL(mailbox); /* no mailbox should have been created */
+ result = pmboxmgr_size(g_manager);
+ CU_ASSERT_EQUAL(result, 0);
+
+ addmsg_id = message_get_id(addmsg);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(addmsg_id);
+ mailbox = pmboxmgr_get_mailbox(g_manager, addmsg_id);
+ CU_ASSERT_PTR_NULL(mailbox); /* no mailbox should have been created */
+
+ etch_object_destroy(addmsg);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_call_4()
+ * test attempt to send a call message that is marked as a reply message
+ */
+static void test_transport_call_4(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_message* addresultmsg = new_add_result_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(addresultmsg);
+ result = message_set_in_reply_to(addresultmsg, new_int64(1)); /* set in reply to ID */
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = g_manager->transport_call(g_manager->imanager, g_who1, addresultmsg, NULL);
+ CU_ASSERT_NOT_EQUAL(result, 0); /* result should indicate error, message is a reply */
+
+ result = pmboxmgr_size(g_manager); /* no mailbox should have been created */
+ CU_ASSERT_EQUAL(result, 0);
+
+ etch_object_destroy(addresultmsg);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_message_1
+ * test reply to a message without a reply to ID
+ */
+void test_session_message_1(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_message* addmsg = NULL;
+
+ my_impl_sessionmessage* my_sessionimpl = g_my_session->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_sessionimpl);
+ CU_ASSERT_EQUAL(my_sessionimpl->what, 0);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->sender);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->msg);
+
+ addmsg = new_add_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(addmsg);
+
+ my_sessionimpl->is_msg_handled = TRUE;
+
+ /* we pass impl rather than interface here - is this right */
+ /* on success we relinquish ownership of the message to the session */
+
+ result = g_manager->session_message(g_manager, g_who1, addmsg);
+
+ CU_ASSERT_EQUAL(result, 0); /* result should indicate message was handled */
+
+ CU_ASSERT_EQUAL(my_sessionimpl->what, SESSION_MESSAGE);
+ CU_ASSERT_PTR_EQUAL(my_sessionimpl->sender, g_who1);
+ CU_ASSERT_PTR_EQUAL(my_sessionimpl->msg, addmsg);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_message_2
+ * test reply to a message without a reply to ID
+ */
+static void test_session_message_2(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_message* addmsg = NULL;
+
+ my_impl_sessionmessage* my_sessionimpl = g_my_session->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_sessionimpl);
+ CU_ASSERT_EQUAL(my_sessionimpl->what, 0);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->sender);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->msg);
+
+ addmsg = new_add_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(addmsg);
+
+ my_sessionimpl->is_msg_handled = FALSE; /* the difference from test 1 */
+
+ /* we pass impl rather than interface here - is this right */
+ /* on failure we retain ownership of the message */
+
+ result = g_manager->session_message (g_manager, g_who1, addmsg);
+
+ CU_ASSERT_NOT_EQUAL_FATAL(result, 0); /* result should indicate message not handled */
+
+ etch_object_destroy(addmsg); /* since session error we still own the message */
+
+ CU_ASSERT_EQUAL(my_sessionimpl->what, SESSION_MESSAGE);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->sender);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->msg);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_message_3
+ * test message having reply to ID not matching any mailbox
+ */
+static void test_session_message_3(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ my_impl_sessionmessage* my_sessionimpl = g_my_session->thisx;
+
+ etch_message* addresultmsg = new_add_result_message();
+ /* no mailbox should be found for this reply to ID */
+ message_set_in_reply_to(addresultmsg, new_int64(1));
+
+ my_sessionimpl->is_msg_handled = TRUE;
+
+ /* we pass impl rather than interface here - is this right */
+ /* on failure we retain ownership of the message */
+ result = g_manager->session_message(g_manager, g_who1, addresultmsg);
+ CU_ASSERT_NOT_EQUAL_FATAL(result, 0); /* result should indicate message not handled */
+
+ etch_object_destroy(addresultmsg); /* since session error we still own the message */
+
+ CU_ASSERT_EQUAL(my_sessionimpl->what, 0);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->sender);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->msg);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_message_5
+ * test message having reply to ID matching an open mailbox
+ */
+static void test_session_message_5(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_int64* msgid = NULL;
+ etch_type* mt_add_result = NULL;
+ etch_mailbox_element* thiselt = NULL;
+ etch_plainmailbox* my_mbox_impl = NULL;
+ i_mailbox* mailbox = NULL, *got_mailbox = NULL;
+ my_impl_sessionmessage* my_sessionimpl = g_my_session->thisx;
+ my_impl_transportmessage* my_transport_impl = g_my_transport->thisx;
+
+ etch_message *replymsg = NULL, *addmsg = new_add_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_sessionimpl);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport_impl);
+
+ g_manager->session_notify(g_manager, new_etch_event(0, ETCHEVT_SESSION_UP));
+
+ result = g_manager->transport_call(g_manager->imanager, g_who1, addmsg, &mailbox);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mailbox);
+
+ CU_ASSERT_EQUAL(my_transport_impl->what, TRANSPORT_MESSAGE);
+ CU_ASSERT_PTR_EQUAL(my_transport_impl->recipient, g_who1);
+ CU_ASSERT_PTR_EQUAL(my_transport_impl->msg, addmsg);
+
+ result = pmboxmgr_size(g_manager);
+ CU_ASSERT_EQUAL(result, 1);
+
+ msgid = message_get_id(addmsg);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msgid);
+ got_mailbox = pmboxmgr_get_mailbox(g_manager, msgid);
+ CU_ASSERT_PTR_EQUAL(got_mailbox, mailbox);
+
+ /* as of now our custom types are not compiled statically. perhaps this will change,
+ * or perhaps we'll simply continue to do it this way in the C binding. */
+ mt_add_result = get_add_result_type();
+
+ /* construct a reply message */
+ replymsg = message_reply(addmsg, mt_add_result);
+ result = is_etch_exception(replymsg);
+ CU_ASSERT_EQUAL_FATAL(result, FALSE);
+ my_sessionimpl->what = WHAT_NONE;
+
+ /* we pass impl rather than interface here - is this right */
+ /* post reply message to mailbox */
+ /* on success we relinquish ownership of the reply message */
+ result = g_manager->session_message(g_manager, g_who1, replymsg);
+ CU_ASSERT_EQUAL_FATAL(result, 0); /* result should indicate message handled */
+
+ CU_ASSERT_EQUAL(my_sessionimpl->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->sender);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->msg);
+
+ /* pop reply message from the mailbox */
+ result = mailbox->read(mailbox, &thiselt);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ CU_ASSERT_PTR_EQUAL(thiselt->whofrom, g_who1);
+ CU_ASSERT_PTR_EQUAL(thiselt->msg, replymsg);
+ etch_object_destroy(thiselt); /* we read it, we own it */
+ replymsg = NULL; /* thiselt destructor destroyed replymsg */
+
+ result = pmboxmgr_size(g_manager);
+ CU_ASSERT_EQUAL(result, 1); /* expect one mailbox */
+
+ result = mailbox->close_read(mailbox);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = pmboxmgr_size(g_manager); /* after close, expect zero mailboxes */
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* in practice the last entity to handle the message would destroy it */
+ etch_object_destroy(addmsg);
+
+ /* it is not yet determined whether the mailbox manager should destroy an
+ * unregistered mailbox - for now it does not, so we destroy it now. */
+ my_mbox_impl = mailbox->thisx; /* get implementation from interface */
+ etch_object_destroy(my_mbox_impl);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_message_6
+ * reply via closed mailbox
+ */
+static void test_session_message_6(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_int64* msgid = NULL;
+ etch_type* mt_add_result = NULL;
+ etch_plainmailbox* my_mbox_impl = NULL;
+ i_mailbox* mailbox = NULL, *got_mailbox = NULL;
+ my_impl_sessionmessage* my_sessionimpl = g_my_session->thisx;
+ my_impl_transportmessage* my_transport_impl = g_my_transport->thisx;
+
+ etch_message *replymsg = NULL, *addmsg = new_add_message();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_sessionimpl);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport_impl);
+
+ g_manager->session_notify(g_manager, new_etch_event(0, ETCHEVT_SESSION_UP));
+
+ result = g_manager->transport_call(g_manager->imanager, g_who1, addmsg, &mailbox);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mailbox);
+
+ CU_ASSERT_EQUAL(my_transport_impl->what, TRANSPORT_MESSAGE);
+ CU_ASSERT_PTR_EQUAL(my_transport_impl->recipient, g_who1);
+ CU_ASSERT_PTR_EQUAL(my_transport_impl->msg, addmsg);
+
+ result = pmboxmgr_size(g_manager);
+ CU_ASSERT_EQUAL(result, 1);
+
+ msgid = message_get_id(addmsg);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msgid);
+ got_mailbox = pmboxmgr_get_mailbox(g_manager, msgid);
+ CU_ASSERT_PTR_EQUAL(got_mailbox, mailbox);
+
+ result = mailbox->close_read(mailbox);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = pmboxmgr_size(g_manager);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* construct a reply message */
+ mt_add_result = get_add_result_type();
+ replymsg = message_reply(addmsg, mt_add_result);
+ result = is_etch_exception(replymsg);
+ CU_ASSERT_EQUAL_FATAL(result, FALSE);
+ my_sessionimpl->what = WHAT_NONE;
+
+ /* we pass impl rather than interface here - is this right */
+ /* on failure we retain ownership of the reply message */
+ result = g_manager->session_message(g_manager, g_who1, replymsg);
+ CU_ASSERT_NOT_EQUAL_FATAL(result, 0); /* result should indicate message not handled */
+ etch_object_destroy(replymsg); replymsg = NULL;
+
+ CU_ASSERT_EQUAL(my_sessionimpl->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->sender);
+ CU_ASSERT_PTR_NULL(my_sessionimpl->msg);
+
+ /* in practice the last entity to handle the message would destroy it */
+ etch_object_destroy(addmsg);
+
+ /* it is not yet determined whether the mailbox manager should destroy an
+ * unregistered mailbox - for now it does not, so we destroy it now. */
+ my_mbox_impl = mailbox->thisx; /* get implementation from interface */
+ etch_object_destroy(my_mbox_impl);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_mailboxmgr_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("mailbox manager test suite", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test test setup", test_test_setup);
+ CU_add_test(pSuite, "test constructor", test_constructor);
+
+ CU_add_test(pSuite, "test transport query", test_transport_query);
+ CU_add_test(pSuite, "test transport control", test_transport_control);
+ CU_add_test(pSuite, "test transport notify", test_transport_notify);
+
+ CU_add_test(pSuite, "test session query", test_session_query);
+ CU_add_test(pSuite, "test session control", test_session_control);
+ CU_add_test(pSuite, "test session notify", test_session_notify);
+
+ CU_add_test(pSuite, "test transport message 1", test_transport_message_1);
+ CU_add_test(pSuite, "test transport message 2", test_transport_message_2);
+ CU_add_test(pSuite, "test transport message 3", test_transport_message_3);
+
+ CU_add_test(pSuite, "test transport call 1", test_transport_call_1);
+ CU_add_test(pSuite, "test transport call 2", test_transport_call_2);
+ CU_add_test(pSuite, "test transport call 3", test_transport_call_3);
+ CU_add_test(pSuite, "test transport call 4", test_transport_call_4);
+
+ CU_add_test(pSuite, "test session message 1", test_session_message_1);
+ CU_add_test(pSuite, "test session message 2", test_session_message_2);
+ CU_add_test(pSuite, "test session message 3", test_session_message_3);
+ CU_add_test(pSuite, "test session message 5", test_session_message_5);
+ CU_add_test(pSuite, "test session message 6", test_session_message_6);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/transport/test_messagizer.c b/binding-c/runtime/c/src/test/transport/test_messagizer.c
new file mode 100644
index 0000000..5525b73
--- /dev/null
+++ b/binding-c/runtime/c/src/test/transport/test_messagizer.c
@@ -0,0 +1,1762 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_messagizer.c
+ */
+#include "etch_runtime.h"
+#include "etch_messagizer.h"
+#include "etch_default_value_factory.h"
+#include "etch_thread.h"
+#include "etch_map.h"
+#include "etch_log.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - -
+ */
+typedef struct test_value_factory test_value_factory;
+
+static etch_messagizer* g_my_messagizer;
+static i_transportpacket* g_my_transportpacket;
+static i_sessionmessage* g_my_sessionmessage;
+static etch_flexbuffer* g_flexbuffer;
+static etch_resources* g_my_resources;
+static vf_idname_map* g_type_map;
+static class_to_type_map* g_class_to_type_map;
+static etch_who* g_who;
+static default_value_factory* g_my_vf;
+static test_value_factory* g_my_test_vf;
+static int g_which_valuefactory;
+
+static unsigned short CLASSID_MY_VF;
+static unsigned short CLASSID_MY_VF_VTAB;
+static unsigned short CLASSID_MY_VF_IMPL;
+static unsigned short CLASSID_MY_IMPL_TP;
+static unsigned short CLASSID_MY_IMPL_SM;
+#define OBJTYPE_MY_IMPL_TP 0x5170
+#define OBJTYPE_MY_IMPL_SM 0x5171
+
+#define is_my_impl_tp(x) (x && ((etch_object*)x)->obj_type == OBJTYPE_MY_IMPL_TP)
+#define is_my_impl_sm(x) (x && ((etch_object*)x)->obj_type == OBJTYPE_MY_IMPL_SM)
+
+#define THISTEST_HEADERSIZE 8
+#define TAGDATA_VERSION 3
+#define TYPECODE_EOD_MARK (-127)
+#define THISTEST_WHO_VALUE 0x5151
+#define WHICHVF_TESTVF 1
+#define WHICHVF_MYVF 2
+#define FAKEID_TYPE_ADD 1
+#define FAKEID_TYPE_ADD_RESULT 2
+#define FAKEID_FIELD_X 3
+#define FAKEID_FIELD_Y 4
+#define FAKEID_FIELD_RESULT 5
+
+static default_value_factory* get_current_valuefactory()
+{
+ if (g_my_test_vf) return (default_value_factory*) g_my_test_vf;
+ if (g_my_vf) return g_my_vf;
+ return NULL;
+}
+
+typedef enum etch_what
+{ WHAT_NONE,
+ TRANSPORT_PACKET, TRANSPORT_QUERY, TRANSPORT_CONTROL, TRANSPORT_NOTIFY,
+ SESSION_MESSAGE, SESSION_QUERY, SESSION_CONTROL, SESSION_NOTIFY
+} etch_what;
+
+
+static int is_equal_who(etch_who* who1, etch_who* who2)
+{
+ int n1 = 0, n2 = 0;
+ if (!who1 || !who2) return FALSE;
+ if (((etch_object*)who1)->class_id != CLASSID_WHO || ((etch_object*)who2)->class_id != CLASSID_WHO) return FALSE;
+ if (!who1->value || !who2->value) return FALSE;
+ if (!is_etch_int32(who1->value) || !is_etch_int32(who2->value)) return FALSE;
+ n1 = ((etch_int32*)who1->value)->value;
+ n2 = ((etch_int32*)who2->value)->value;
+ return n1 == n2;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * my_impl_transportpacket (i_transportpacket implementation)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * my_impl_transportpacket
+ * test object implementing i_transportpacket
+ */
+typedef struct my_impl_transportpacket
+{
+ etch_object object;
+
+ /* i_transportpacket interface and methods, plus original destructor
+ * which becomes replaced with a custom destructor to destroy this
+ * object. this is the model for destroying an interface wrapper object
+ * when we do not save and pass around a pointer to the wrapper, but rather
+ * a pointer to the interface. the interface in question, i_transportpacket
+ * in this case, contains a pointer to the wrapper object, in this case a
+ * my_impl_transportpacket*. when the interface is instantiated, its original
+ * destructor is saved, and is replaced with a destructor which invokes
+ * the wrapper's destructor. the wrapper destructor must then know to
+ * invoke the interface's original destructor when destroying the interface.
+ */
+ i_transportpacket* ixp; /* owned */
+ etch_object_destructor destroy_transportpacket; /* i_transportpacket original destructor */
+ etch_transport_packet transport_packet; /* transport_packet() */
+ etch_transport_packet_headersize header_size; /* header_size() */
+
+ i_sessionpacket* session; /* not owned */
+
+ etch_who* recipient; /* not owned */
+ etch_what what;
+ size_t bufcount;
+ char* buf; /* owned */
+ etch_object* query; /* owned */
+ etch_object* query_result; /* owned */
+ etch_object* control; /* owned */
+ etch_object* value; /* owned */
+ etch_object* eventx; /* owned */
+
+} my_impl_transportpacket;
+
+
+/**
+ * destroy_my_impl_transportpacket()
+ * my_impl_transportpacket destructor
+ */
+static int destroy_my_impl_transportpacket(void* data)
+{
+ my_impl_transportpacket* thisx = (my_impl_transportpacket*)data;
+ assert(is_my_impl_tp(thisx));
+
+ if (!is_etchobj_static_content(thisx))
+ { /* invoke original i_transportpacket destructor */
+ if (thisx->ixp && thisx->destroy_transportpacket)
+ thisx->destroy_transportpacket(thisx->ixp);
+
+ if (thisx->buf)
+ etch_free(thisx->buf);
+
+
+ etch_object_destroy(thisx->query);
+ etch_object_destroy(thisx->query_result);
+ etch_object_destroy(thisx->control);
+ etch_object_destroy(thisx->value);
+ etch_object_destroy(thisx->eventx);
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * impl_transport_packet()
+ * my_impl_transportpacket::transport_packet
+ * @param whoto caller retains, can be null
+ * @param fbuf caller retains
+ */
+static int impl_transport_packet (my_impl_transportpacket* mytp, etch_who* whoto, etch_flexbuffer* fbuf)
+{
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ mytp->what = TRANSPORT_PACKET;
+ mytp->recipient = whoto;
+ /* retrieve the packet data. don't skip over header (0 is skip bytes) */
+ mytp->buf = (char*)etch_flexbuf_get_allfrom(fbuf, 0, &mytp->bufcount);
+ return 0;
+}
+
+
+/**
+ * my_transport_control()
+ * my_impl_transportpacket::itransport::transport_control
+ */
+static int my_transport_control (my_impl_transportpacket* mytp, etch_object* control, etch_object* value)
+{
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ mytp->what = TRANSPORT_CONTROL;
+ mytp->control = control;
+ mytp->value = value;
+ return 0;
+}
+
+
+/**
+ * my_transport_notify()
+ * my_impl_transportpacket::itransport::transport_notify
+ */
+static int my_transport_notify (my_impl_transportpacket* mytp, etch_object* evt)
+{
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ mytp->what = TRANSPORT_NOTIFY;
+ mytp->eventx = evt;
+ return 0;
+}
+
+
+/**
+ * my_transport_query()
+ * my_impl_transportpacket::itransport::transport_query
+ */
+static etch_object* my_transport_query (my_impl_transportpacket* mytp, etch_object* query)
+{
+ etch_object* resultobj = NULL;
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ resultobj = mytp->query_result; /* set artificially in test */
+ mytp->what = TRANSPORT_QUERY;
+ mytp->query = query;
+ mytp->query_result = NULL;
+ return (etch_object*) resultobj; /* caller owns */
+}
+
+
+/**
+ * my_transport_get_session()
+ * my_impl_transportpacket::itransport::get_session
+ */
+static i_sessionpacket* my_transport_get_session(my_impl_transportpacket* mytp)
+{
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ return mytp->session;
+}
+
+
+/**
+ * my_transport_set_session()
+ * my_impl_transportpacket::itransport::set_session
+ */
+static void my_transport_set_session(my_impl_transportpacket* mytp, i_sessionpacket* session)
+{
+ CU_ASSERT_FATAL(is_my_impl_tp(mytp));
+ CU_ASSERT_FATAL(is_etch_sessionpacket(session));
+ mytp->session = session;
+}
+
+
+/*
+ * destroy_my_transportpacket()
+ * i_transportpacket destructor
+ * this destructor will destroy its parent (my_impl_transportpacket),
+ * which will in turn destroy this object.
+ */
+static int destroy_my_transportpacket(void* data)
+{
+ i_transportpacket* itp = (i_transportpacket*)data;
+ my_impl_transportpacket* mytp = NULL;
+ assert(is_etch_transportpkt(itp));
+
+ mytp = itp->thisx;
+ assert(is_my_impl_tp(mytp));
+
+ etch_object_destroy(mytp);
+
+ return 0;
+}
+
+
+/**
+ * new_my_impl_transportpacket()
+ * my_impl_transportpacket constructor
+ */
+static my_impl_transportpacket* new_my_impl_transportpacket()
+{
+ i_transportpacket* itp = NULL;
+ i_transport* itransport = NULL;
+ /* this is a model for dynamic class ID assigment */
+ unsigned short class_id = get_dynamic_classid_unique(&CLASSID_MY_IMPL_TP);
+
+ my_impl_transportpacket* mytp = (my_impl_transportpacket*) new_object
+ (sizeof(my_impl_transportpacket), OBJTYPE_MY_IMPL_TP, class_id);
+
+ ((etch_object*)mytp)->destroy = destroy_my_impl_transportpacket;
+
+ itransport = new_transport_interface_ex(mytp,
+ (etch_transport_control) my_transport_control,
+ (etch_transport_notify) my_transport_notify,
+ (etch_transport_query) my_transport_query,
+ (etch_transport_get_session) my_transport_get_session,
+ (etch_transport_set_session) my_transport_set_session);
+
+ itp = new_transportpkt_interface(mytp, (void*)impl_transport_packet, itransport);
+
+ /* default header_size() will return this value so no need to override */
+ itp->header_size = THISTEST_HEADERSIZE;
+
+ /* save off i_transportpacket destructor */
+ mytp->destroy_transportpacket = ((etch_object*)itp)->destroy;
+
+ /* replace i_transportpacket destructor with one which will destroy this object */
+ ((etch_object*)itp)->destroy = destroy_my_transportpacket;
+
+ /* g_my_transportpacket will get set to this interface */
+ mytp->ixp = itp;
+
+ return mytp;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * my_impl_sessionmessage (i_sessionmessage implementation)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * my_impl_sessionmessage
+ * test object implementing i_sessionmessage
+ */
+typedef struct my_impl_sessionmessage
+{
+ etch_object object;
+ /*
+ * i_sessionmessage interface and methods, plus original destructor
+ * which becomes replaced with a custom destructor to destroy this
+ * object. this is the model for destroying an interface wrapper object
+ * when we do not save and pass around a pointer to the wrapper, but rather
+ * a pointer to the interface. the interface in question, i_sessionmessage
+ * in this case, contains a pointer to the wrapper object, in this case a
+ * my_impl_sessionmessage*. when the interface is instantiated, its original
+ * destructor is saved, and is replaced with a destructor which invokes
+ * the wrapper's destructor. the wrapper destructor must then know to
+ * invoke the interface's original destructor when destroying the interface.
+ */
+ i_sessionmessage* ism; /* owned */
+ etch_object_destructor destroy_sessionmessage; /* i_sessionmessage original destructor */
+ etch_session_message session_message; /* session_message() method */
+
+ etch_what what;
+ etch_who* sender; /* not owned */
+ etch_message* msg; /* not owned */
+ int is_msg_handled;
+ etch_object* query; /* owned */
+ etch_object* query_result; /* owned */
+ etch_object* control; /* owned */
+ etch_object* value; /* owned */
+ etch_object* eventx; /* owned */
+
+ i_sessionpacket* session;
+
+} my_impl_sessionmessage;
+
+
+
+/**
+ * destroy_my_impl_sessionmessage()
+ * my_impl_sessionmessage destructor
+ */
+static int destroy_my_impl_sessionmessage(void* data)
+{
+
+ my_impl_sessionmessage* thisx = (my_impl_sessionmessage*)data;
+ if (!is_etchobj_static_content(thisx))
+ { /* invoke original i_sessionmessage destructor */
+ if (thisx->ism && thisx->destroy_sessionmessage)
+ thisx->destroy_sessionmessage(thisx->ism);
+
+ /* these are objects which would be destroyed in the binding
+ * by the last method to touch them */
+ etch_object_destroy(thisx->msg);
+
+ etch_object_destroy(thisx->query);
+
+ etch_object_destroy(thisx->query_result);
+
+ etch_object_destroy(thisx->control);
+
+ etch_object_destroy(thisx->value);
+
+ etch_object_destroy(thisx->eventx);
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * impl_session_message()
+ * my_impl_sessionmessage::ism::session_message.
+ * @param whofrom caller retains, can be null.
+ * @param msg caller abandons
+ */
+static int impl_session_message (my_impl_sessionmessage* mysm, etch_who* whofrom, etch_message* msg)
+{
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ mysm->what = SESSION_MESSAGE;
+
+ /* in this emulation we are the session consuming a message. if successful,
+ * (i.e., the message is handled), the binding will eventually destroy the
+ * message (the caller relinquishes message memory), and the who object.
+ * if not successful (message not handled) the caller retains message and
+ * who memory in order to forward the message and who somewhere else
+ * (as an unwanted message). so we model that here: if the message is not
+ * handled (a manual switch in these tests), we do not save references to
+ * the messaqe and who for cleanup, because the unwanted message, containing
+ * these objects, will be cleaned up instead.
+ */
+ if (mysm->is_msg_handled)
+ {
+ mysm->msg = msg;
+ mysm->sender = whofrom;
+ return 0;
+ }
+
+ mysm->msg = NULL;
+ mysm->sender = NULL;
+ return -1;
+}
+
+
+/**
+ * my_session_control()
+ * my_impl_sessionmessage::ism::isession::session_control
+ * control and value are always abandoned by caller so mysm must clean them up.
+ */
+static int my_session_control (my_impl_sessionmessage* mysm, etch_object* control, etch_object* value)
+{
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ mysm->what = SESSION_CONTROL;
+ mysm->control = control;
+ mysm->value = value;
+ return 0;
+}
+
+
+/**
+ * my_session_notify()
+ * my_impl_sessionmessage::ism::isession::session_notify
+ * evt is always abandoned by caller so mysm must clean it up.
+ */
+static int my_session_notify (my_impl_sessionmessage* mysm, etch_object* evt)
+{
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ mysm->what = SESSION_NOTIFY;
+ mysm->eventx = evt;
+ return 0;
+}
+
+
+/**
+ * my_session_query()
+ * my_impl_sessionmessage::ism::isession::session_query
+ * query is always abandoned by caller so mysm must clean it up.
+ */
+static etch_object* my_session_query (my_impl_sessionmessage* mysm, etch_object* query)
+{
+ etch_object* resultobj = NULL;
+ CU_ASSERT_FATAL(is_my_impl_sm(mysm));
+ resultobj = mysm->query_result; /* artifically set in test */
+ mysm->what = SESSION_QUERY;
+ mysm->query = query;
+ mysm->query_result = NULL;
+ return (etch_object*) resultobj; /* caller owns */
+}
+
+
+/*
+ * destroy_my_sessionmessage()
+ * i_sessionmessage destructor
+ * this destructor will destroy its parent (my_impl_sessionmessage),
+ * which will in turn destroy this object.
+ */
+static int destroy_my_sessionmessage(void* data)
+{
+ i_sessionmessage* ism = (i_sessionmessage*)data;
+ my_impl_sessionmessage* mysm = NULL;
+ if (NULL == ism) return -1;
+
+ mysm = ism->thisx;
+
+ etch_object_destroy(mysm);
+
+ return 0;
+}
+
+
+/**
+ * new_my_impl_sessionmessage()
+ * my_impl_sessionmessage constructor
+ */
+static my_impl_sessionmessage* new_my_impl_sessionmessage()
+{
+ i_sessionmessage* ism = NULL;
+ i_session* isession = NULL;
+ /* this is a model for dynamic class ID assigment */
+ unsigned short class_id = CLASSID_MY_IMPL_SM? CLASSID_MY_IMPL_SM:
+ (CLASSID_MY_IMPL_SM = get_dynamic_classid());
+
+ my_impl_sessionmessage* mysm = (my_impl_sessionmessage*) new_object
+ (sizeof(my_impl_sessionmessage), OBJTYPE_MY_IMPL_SM, class_id);
+
+ ((etch_object*)mysm)->destroy = destroy_my_impl_sessionmessage;
+
+ isession = new_session_interface(mysm,
+ (etch_session_control) my_session_control,
+ (etch_session_notify) my_session_notify,
+ (etch_session_query) my_session_query);
+
+ ism = new_sessionmsg_interface(mysm, (void*)impl_session_message, isession);
+
+ /* save off i_sessionmessage destructor */
+ mysm->destroy_sessionmessage = ((etch_object*)ism)->destroy;
+
+ /* custom destructor will destroy the my_impl_sessionmessage */
+ ((etch_object*)ism)->destroy = destroy_my_sessionmessage;
+
+ /* g_my_sessionmessage will get set to this interface */
+ mysm->ism = ism;
+
+ return mysm;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * test value factories
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_value_factory
+ * value factory version 1
+ * this version uses inherited data coded into the object, as opposed to
+ * instantiating an impl object to contain inherited data and methods.
+ */
+//typedef struct test_value_factory
+struct test_value_factory
+{
+ etch_object object;
+ /* - - - - - - - - - - - - -
+ * default value factory
+ * - - - - - - - - - - - - -
+ */
+ etch_object* impl;
+
+ class_to_type_map* class_to_type;
+ vf_idname_map* types;
+ etch_arraylist* mixins;
+
+ unsigned char is_own_types;
+ unsigned char is_own_class_to_type;
+
+ /* - - - - - - - - - - - - -
+ * test value factory
+ * - - - - - - - - - - - - -
+ */
+ etch_type* mt_add;
+ etch_type* mt_add_result;
+ etch_field* mf_x;
+ etch_field* mf_y;
+ etch_field* mf_result;
+};
+//} test_value_factory;
+
+
+/**
+ * destroy_test_value_factory()
+ * destructor for value factory version 1
+ */
+static int destroy_test_value_factory(void* data)
+{
+ test_value_factory* vf = (test_value_factory*)data;
+ if (NULL == vf) return -1;
+
+ if (!is_etchobj_static_content(vf))
+ {
+ destroy_static_type(vf->mt_add);
+ destroy_static_type(vf->mt_add_result);
+ destroy_static_field(vf->mf_x);
+ destroy_static_field(vf->mf_y);
+ destroy_static_field(vf->mf_result);
+ }
+
+ return destroy_default_value_factory((default_value_factory*) vf);
+}
+
+
+/**
+ * new_test_valuefactory()
+ * constructor for value factory version 1 inheriting from default_value_factory
+ */
+static test_value_factory* new_test_valuefactory()
+{
+ etchparentinfo* inheritlist = NULL;
+ test_value_factory* vf = NULL;
+
+ g_type_map = new_vf_types_collection(ETCH_DEFSIZE);
+ /* since we explicitly instantiate a type map, and since we explicitly destroy
+ * the test's custom types, we want the type maps destructor to not destroy
+ * the map content. overriding the map's content clear callback is one way
+ * to do this. */
+ g_type_map->freehook = etch_noop_clear_handler;
+ g_class_to_type_map = new_class_to_type_map(ETCH_DEFSIZE);
+
+ /* instantiate the new value factory.
+ * this vf does NOT own its type maps since we supply them here.
+ * however if we wanted to abandon ownership, we could set the
+ * vf.is_own_types and vf.is_own_class_to_type flags here.
+ */
+ // init typemap and class to type map
+ defvf_initialize_static(g_type_map, g_class_to_type_map);
+ vf = (test_value_factory*) new_default_value_factory_ex(sizeof(test_value_factory), g_type_map, g_class_to_type_map);
+
+ ((etch_object*)vf)->destroy = destroy_test_value_factory;
+
+ /* ensure parent type keys exist in the (one-based) inheritance list.
+ * parent class of our custom vf is default_value_factory.
+ * inheritance list is used by validators and object assignment logic.
+ */
+ inheritlist = get_vtab_inheritance_list((etch_object*)vf, 2, 1, CLASSID_MY_VF_VTAB);
+ inheritlist[1].o.obj_type = ETCHTYPEB_VALUEFACTORY;
+ inheritlist[1].c.class_id = CLASSID_VALUEFACTORY; /* parent class */
+ ((etch_object*)vf)->class_id = CLASSID_MY_VF; /* our class */
+
+ /* instantiate the custom vf's instance data and assign it to the vf.
+ */
+ vf->mt_add = new_static_type(L"add");
+ vf->mt_add_result = new_static_type(L"add_result");
+ vf->mf_x = new_static_field(L"x");
+ vf->mf_y = new_static_field(L"y");
+ vf->mf_result = new_static_field(L"xresult");
+
+ /* we replace generated ids with 1-byte IDs to make test data buffers easier to construct */
+ vf->mt_add->id = FAKEID_TYPE_ADD;
+ vf->mt_add_result->id = FAKEID_TYPE_ADD_RESULT;
+ vf->mf_x->id = FAKEID_FIELD_X;
+ vf->mf_y->id = FAKEID_FIELD_Y;
+ vf->mf_result->id = FAKEID_FIELD_RESULT;
+
+ ((struct i_value_factory*)((etch_object*)vf)->vtab)->add_type(vf, vf->mt_add);
+ ((struct i_value_factory*)((etch_object*)vf)->vtab)->add_type(vf, vf->mt_add_result);
+
+ etchtype_put_validator(vf->mt_add, clone_field(vf->mf_x),
+ (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(vf->mt_add, clone_field(vf->mf_y),
+ (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(vf->mt_add, clone_field(builtins._mf__message_id),
+ (etch_object*) etchvtor_int64_get(0));
+
+ etchtype_put_validator(vf->mt_add_result, clone_field(vf->mf_result),
+ (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(vf->mt_add_result, clone_field(builtins._mf__message_id),
+ (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(vf->mt_add_result, clone_field(builtins._mf__in_reply_to),
+ (etch_object*) etchvtor_int64_get(0));
+
+ g_my_test_vf = vf;
+ return g_my_test_vf;
+}
+
+
+/**
+ * my_valufactory_impl
+ * value factory version 2 instance data object
+ */
+typedef struct my_valufactory_impl
+{
+ etch_object object;
+
+ etch_type* mt_add;
+ etch_type* mt_add_result;
+ etch_field* mf_x;
+ etch_field* mf_y;
+ etch_field* mf_result;
+
+} my_valufactory_impl;
+
+
+/**
+ * destroy_my_valufactory_impl()
+ * destructor for inheriting value factory version 2 instance data
+ */
+static int destroy_my_valufactory_impl(void* data)
+{
+ my_valufactory_impl* impl = (my_valufactory_impl*)data;
+ if (NULL == impl) return -1;
+
+ if (!is_etchobj_static_content(impl))
+ {
+ destroy_static_type(impl->mt_add);
+ destroy_static_type(impl->mt_add_result);
+ destroy_static_field(impl->mf_x);
+ destroy_static_field(impl->mf_y);
+ destroy_static_field(impl->mf_result);
+ }
+
+ return destroy_objectex((etch_object*) impl);
+}
+
+
+/**
+ * new_my_valufactory_impl()
+ * constructor for inheriting value factory version 2 instance data
+ */
+static my_valufactory_impl* new_my_valufactory_impl()
+{
+ unsigned short class_id = CLASSID_MY_VF_IMPL? CLASSID_MY_VF_IMPL:
+ (CLASSID_MY_VF_IMPL = get_dynamic_classid());
+
+ my_valufactory_impl* impl = (my_valufactory_impl*) new_object
+ (sizeof(my_valufactory_impl), ETCHTYPEB_VALUEFACTIMP, class_id);
+
+ ((etch_object*)impl)->destroy = destroy_my_valufactory_impl;
+
+ impl->mt_add = new_static_type(L"add");
+ impl->mt_add_result = new_static_type(L"add_result");
+ impl->mf_x = new_static_field(L"x");
+ impl->mf_y = new_static_field(L"y");
+ impl->mf_result = new_static_field(L"xresult");
+
+ /* we replace generated ids with 1-byte IDs to make test data buffers easier to construct */
+ impl->mt_add->id = FAKEID_TYPE_ADD;
+ impl->mt_add_result->id = FAKEID_TYPE_ADD_RESULT;
+ impl->mf_x->id = FAKEID_FIELD_X;
+ impl->mf_y->id = FAKEID_FIELD_Y;
+ impl->mf_result->id = FAKEID_FIELD_RESULT;
+
+ etchtype_put_validator(impl->mt_add, clone_field(impl->mf_x),
+ (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(impl->mt_add, clone_field(impl->mf_y),
+ (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(impl->mt_add, clone_field(builtins._mf__message_id),
+ (etch_object*) etchvtor_int64_get(0));
+
+ etchtype_put_validator(impl->mt_add_result, clone_field(impl->mf_result),
+ (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(impl->mt_add_result, clone_field(builtins._mf__message_id),
+ (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(impl->mt_add_result, clone_field(builtins._mf__in_reply_to),
+ (etch_object*) etchvtor_int64_get(0));
+
+ return impl;
+}
+
+
+/**
+ * new_bogus_valuefactory()
+ * constructor for value factory version 2 inheriting from default_value_factory
+ */
+static default_value_factory* new_bogus_valuefactory()
+{
+ etchparentinfo* inheritlist = NULL;
+ my_valufactory_impl* impl = NULL;
+
+
+ g_type_map = new_vf_types_collection(ETCH_DEFSIZE);
+ /* since we explicitly instantiate a type map, and since we explicitly destroy
+ * the test's custom types, we want the type maps destructor to not destroy
+ * the map content. overriding the map's content clear callback is one way
+ * to do this. */
+ g_type_map->freehook = etch_noop_clear_handler;
+ g_class_to_type_map = new_class_to_type_map(ETCH_DEFSIZE);
+
+ /* instantiate the new value factory.
+ * this vf does NOT own its type maps since we supply them here.
+ * however if we wanted to abandon ownership, we could clear the
+ * vf.is_own_types and vf.is_own_class_to_type flags here.
+ */
+ defvf_initialize_static(g_type_map, g_class_to_type_map);
+ g_my_vf = new_default_value_factory(g_type_map, g_class_to_type_map);
+
+ /* ensure parent type keys exist in the (one-based) inheritance list.
+ * parent class of our custom vf is default_value_factory.
+ * inheritance list is used by validators and object assignment logic.
+ */
+ inheritlist = get_vtab_inheritance_list((etch_object*)g_my_vf, 2, 1, CLASSID_MY_VF_VTAB);
+ inheritlist[1].o.obj_type = ETCHTYPEB_VALUEFACTORY;
+ inheritlist[1].c.class_id = CLASSID_VALUEFACTORY; /* parent class */
+ ((etch_object*)g_my_vf)->class_id = CLASSID_MY_VF; /* our class */
+
+ /* instantiate the custom vf's instance data and assign it to the vf.
+ * the impl comprises all data specific to the inheriting class, including
+ * data and methods if any. the default value factory destructor will call
+ * the destructor on the vf's impl object.
+ */
+ impl = new_my_valufactory_impl();
+ g_my_vf->impl = (etch_object*) impl;
+
+ ((struct i_value_factory*)((etch_object*)g_my_vf)->vtab)->add_type(g_my_vf, impl->mt_add);
+ ((struct i_value_factory*)((etch_object*)g_my_vf)->vtab)->add_type(g_my_vf, impl->mt_add_result);
+
+ return g_my_vf;
+}
+
+
+/**
+ * get_vftype()
+ * return a type depending on which version of value factory is instantiated
+ */
+static etch_type* get_vftype(const int typeid)
+{
+ etch_type* type = NULL;
+
+ if (g_my_vf)
+ {
+ my_valufactory_impl* impl = (my_valufactory_impl*) g_my_vf->impl;
+
+ if (typeid == impl->mt_add->id)
+ type = impl->mt_add;
+ else
+ if (typeid == impl->mt_add_result->id)
+ type = impl->mt_add_result;
+ }
+ else
+ if (g_my_test_vf)
+ {
+ if (typeid == g_my_test_vf->mt_add->id)
+ type = g_my_test_vf->mt_add;
+ else
+ if (typeid == g_my_test_vf->mt_add_result->id)
+ type = g_my_test_vf->mt_add_result;
+ }
+
+ return type;
+}
+
+
+/* - - - - - - - - - - - - - - - - -
+ * test messagizer
+ * - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_bogus_messagizer()
+ */
+static etch_messagizer* new_bogus_messagizer(i_transportpacket* transport)
+{
+ g_my_messagizer = new_messagizer(transport, L"foo:?Messagizer.format=binary", g_my_resources);
+ return g_my_messagizer;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * individual test data setup and teardown
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * setup_this_test()
+ */
+static int setup_this_test(const int which_valuefactory)
+{
+ my_impl_transportpacket* mytp_impl = NULL;
+ my_impl_sessionmessage* mysm_impl = NULL;
+ default_value_factory* thisvf = NULL;
+ g_which_valuefactory = which_valuefactory;
+
+ /* two versions of value factory using different inheritance models */
+ if (which_valuefactory == WHICHVF_TESTVF) /* vf version 1 inline inheritance */
+ { thisvf = (default_value_factory*) new_test_valuefactory();
+ set_etchobj_static_all(g_my_test_vf); /* so resources will not destroy */
+ }
+ else
+ {
+ thisvf = new_bogus_valuefactory(); /* vf version 2 impl object inheritance */
+ set_etchobj_static_all(g_my_vf); /* so resources will not destroy */
+ }
+
+ g_my_resources = new_etch_resources(ETCH_DEFSIZE);
+ etch_resources_add(g_my_resources, ETCH_RESXKEY_MSGIZER_VALUFACT, (etch_object*) thisvf);
+
+ /* we instantiate a wrapper x which implements and instantiates i_transportpacket.
+ * the instantiation of i_transportpacket will contain a pointer to x.
+ * our global reference g_my_transportpacket is a pointer to the interface.
+ * the purpose of this excercise is that, in the real binding we can pass
+ * around the interface, whose methods can be then invoked without knowing
+ * anything about the wrapper. when we want to reference the wrapper x,
+ * it is (my_impl_transportpacket) g_my_transportpacket->thisx.
+ */
+ mytp_impl = new_my_impl_transportpacket();
+ g_my_transportpacket = mytp_impl->ixp;
+
+ /* we instantiate a wrapper y which implements and instantiates i_sessionmessage.
+ * the instantiation of i_sessionmessage will contain a pointer to y.
+ * our global reference g_my_sessionmessage is a pointer to the interface.
+ * the purpose of this excercise is that, in the real binding we can pass
+ * around the interface, whose methods can be then invoked without knowing
+ * anything about the wrapper. when we want to reference the wrapper x,
+ * it is (my_impl_sessionmessage*) g_my_sessionmessage->thisx.
+ */
+ mysm_impl = new_my_impl_sessionmessage();
+ g_my_sessionmessage = mysm_impl->ism;
+
+ g_who = new_who(new_int32(THISTEST_WHO_VALUE));
+
+ /* finally instantiate the test messagizer */
+ new_bogus_messagizer(g_my_transportpacket);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_my_messagizer);
+ etch_msgizer_set_session(g_my_messagizer, g_my_sessionmessage);
+
+ return 0;
+}
+
+
+/**
+ * teardown_this_test()
+ */
+static int teardown_this_test()
+{
+ etch_object_destroy(g_my_messagizer);
+
+ etch_object_destroy(g_my_transportpacket);
+
+ etch_object_destroy(g_my_sessionmessage);
+
+ etch_object_destroy(g_my_resources);
+
+ if (g_my_vf)
+ { clear_etchobj_static_all(g_my_vf);
+ etch_object_destroy(g_my_vf);
+ }
+ else /* can only instantiate one or the other */
+ if (g_my_test_vf)
+ { clear_etchobj_static_all(g_my_test_vf);
+ etch_object_destroy(g_my_test_vf);
+ }
+
+ etch_object_destroy(g_who);
+
+ etch_object_destroy(g_type_map); // ************************************************
+ etch_object_destroy(g_class_to_type_map);
+
+ if (g_flexbuffer)
+ etch_object_destroy(g_flexbuffer);
+
+ g_my_transportpacket = NULL;
+ g_my_sessionmessage = NULL;
+ g_class_to_type_map = NULL;
+ g_my_messagizer = NULL;
+ g_my_resources = NULL;
+ g_flexbuffer = NULL;
+ g_my_test_vf = NULL;
+ g_type_map = NULL;
+ g_my_vf = NULL;
+ g_who = NULL;
+
+ etchvf_free_builtins();
+
+ return 0;
+}
+
+
+/* - - - - - - - - - - - - - -
+ * unit tests
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_transportpacket_constructor()
+ */
+static void test_transportpacket_constructor(void)
+{
+ my_impl_transportpacket* mytp_impl = new_my_impl_transportpacket();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mytp_impl);
+
+ /* the custom interface object destructors are coded to destroy their
+ * implementing objects, so we do that here to verify this functionality.
+ */
+ do
+ { i_transportpacket* itp = mytp_impl->ixp;
+ etch_object_destroy(itp);
+
+ } while(0);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_sessionmessage_constructor()
+ */
+static void test_sessionmessage_constructor(void)
+{
+ my_impl_sessionmessage* mysm_impl = new_my_impl_sessionmessage();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mysm_impl);
+
+ /* the custom interface object destructors are coded to destroy their
+ * implementing objects, so we do that here to verify this functionality.
+ */
+ do
+ { i_sessionmessage* ism = mysm_impl->ism;
+ etch_object_destroy(ism);
+
+ } while(0);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_messagizer_constructor()
+ */
+static void test_messagizer_constructor(void)
+{
+ etch_messagizer* mzr = NULL;
+ i_transportpacket* itp = NULL;
+ etch_resources* resxmap = NULL;
+ default_value_factory* vf = NULL;
+ vf_idname_map* typemap = NULL;
+ class_to_type_map* c2tmap = NULL;
+ my_impl_transportpacket* mytp_impl = NULL;
+
+ typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(typemap);
+ c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(c2tmap);
+ defvf_initialize_static(typemap, c2tmap);
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(vf);
+
+ mytp_impl = NULL;
+
+ vf = new_default_value_factory(typemap, c2tmap);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(vf);
+ set_etchobj_static_all(vf); /* so resources will not destroy */
+
+ resxmap = new_etch_resources(ETCH_DEFSIZE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(resxmap);
+ etch_resources_add(resxmap, ETCH_RESXKEY_MSGIZER_VALUFACT, (etch_object*) vf);
+
+ mytp_impl = new_my_impl_transportpacket();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mytp_impl);
+
+ itp = mytp_impl->ixp;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(itp);
+
+ /* messagizer does not own i_transportpacket* itp */
+ mzr = new_messagizer(itp, L"foo:?Messagizer.format=binary", resxmap);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mzr);
+
+ etch_object_destroy(mzr);
+
+ /* i_transportpacket.destroy() will destroy my_impl_transportpacket */
+ etch_object_destroy(itp);
+
+ etch_object_destroy(resxmap);
+ clear_etchobj_static_all(vf); /* so we can destroy it now */
+
+ etch_object_destroy(vf);
+ etch_object_destroy(c2tmap);
+ etch_object_destroy(typemap);
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_testsetup_teardown_a
+ */
+static void test_testsetup_teardown_a(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_testsetup_teardown_b
+ */
+static void test_testsetup_teardown_b(void)
+{
+ setup_this_test(WHICHVF_MYVF);
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_packet_1
+ * mimics the transport messagizing a packet and delivering the message to the session
+ */
+static void test_packet_1(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ etch_type* msgtype = NULL;
+ const int THISTEST_BUFSIZE = 4;
+ char* buf = etch_malloc(THISTEST_BUFSIZE, ETCHTYPEB_BYTES);
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+
+ /* load up the packet buffer with test data */
+ buf[0] = TAGDATA_VERSION; buf[1] = FAKEID_TYPE_ADD; buf[2] = 0; buf[3] = TYPECODE_EOD_MARK;
+ g_flexbuffer = new_flexbuffer_from(buf, THISTEST_BUFSIZE, THISTEST_BUFSIZE, 0);
+
+ my_session->is_msg_handled = TRUE;
+
+ /* messagize the packet and deliver the message to the session */
+ result = g_my_messagizer->session_packet (g_my_messagizer, g_who, g_flexbuffer);
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result) break;
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_MESSAGE);
+ result = is_equal_who(my_session->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+ CU_ASSERT_EQUAL(message_size(my_session->msg), 0);
+ CU_ASSERT_PTR_NULL(my_session->eventx);
+ /* assert that message type is "add" (since we buffered FAKEID_TYPE_ADD above) */
+ msgtype = message_type(my_session->msg);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msgtype);
+ CU_ASSERT_EQUAL(msgtype->id, FAKEID_TYPE_ADD);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_packet_2
+ * mimics the transport messagizing a packet and delivering the message to the session
+ * which rejects the message and forwards the message as rejected.
+ */
+static void test_packet_2(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ etch_type* msgtype = NULL;
+ const int THISTEST_BUFSIZE = 4;
+ etch_message* thismessage = NULL;
+ etch_unwanted_message* uwmsg = NULL;
+ char* buf = etch_malloc(THISTEST_BUFSIZE, ETCHTYPEB_BYTES);
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+
+ /* load up the packet buffer with test data */
+ buf[0] = TAGDATA_VERSION; buf[1] = FAKEID_TYPE_ADD; buf[2] = 0; buf[3] = TYPECODE_EOD_MARK;
+ g_flexbuffer = new_flexbuffer_from(buf, THISTEST_BUFSIZE, THISTEST_BUFSIZE, 0);
+
+ /* for the purposes of this test we manually specify that the message was not handled.
+ * when this is the case, the message and who will be wrapped up in an "unwanted message"
+ * which is forwarded to the session as an event. when this is the case, memory for the
+ * message and who is not cleaned up by the session in the normal manner, but is instead
+ * owned by the unwanted message, and destroyed with that object.
+ */
+ my_session->is_msg_handled = FALSE;
+
+ /* messagize the packet and deliver the message to the session */
+ result = g_my_messagizer->session_packet (g_my_messagizer, g_who, g_flexbuffer);
+ CU_ASSERT_EQUAL(result, -1);
+ if (-1 != result) break;
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_NOTIFY);
+ CU_ASSERT_EQUAL(message_size(my_session->msg), 0);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->eventx);
+
+ uwmsg = (etch_unwanted_message*) my_session->eventx;
+ CU_ASSERT_EQUAL_FATAL(((etch_object*)uwmsg)->class_id, CLASSID_EVENT_UNWANTMSG);
+
+ /* find the message and who in the "unwanted message" wrapper */
+ result = is_equal_who(uwmsg->whofrom, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ thismessage = uwmsg->message;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thismessage);
+
+ /* assert that message type is "add" (since we buffered FAKEID_TYPE_ADD above) */
+ msgtype = message_type(thismessage);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msgtype);
+ CU_ASSERT_EQUAL(msgtype->id, FAKEID_TYPE_ADD);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_packet_3
+ * mimics the transport messagizing a packet and delivering the message to the session
+ */
+static void test_packet_3(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ etch_type* msgtype = NULL;
+ const int THISTEST_BUFSIZE = 4;
+ char* buf = etch_malloc(THISTEST_BUFSIZE, ETCHTYPEB_BYTES);
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+
+ /* load up the packet buffer with test data */
+ buf[0] = TAGDATA_VERSION;
+ buf[1] = FAKEID_TYPE_ADD_RESULT;
+ buf[2] = 0;
+ buf[3] = TYPECODE_EOD_MARK;
+
+ g_flexbuffer = new_flexbuffer_from(buf, THISTEST_BUFSIZE, THISTEST_BUFSIZE, 0);
+
+ /* for the purposes of this test we manually specify that the message was handled */
+ my_session->is_msg_handled = TRUE;
+
+ /* messagize the packet and deliver the message to the session */
+ result = g_my_messagizer->session_packet (g_my_messagizer, g_who, g_flexbuffer);
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result) break;
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_MESSAGE);
+ CU_ASSERT_EQUAL(message_size(my_session->msg), 0);
+
+ /* assert that message type is "add_result" (since we buffered FAKEID_TYPE_ADD_RESULT above) */
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->msg);
+ msgtype = message_type(my_session->msg);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(msgtype);
+ CU_ASSERT_EQUAL(msgtype->id, FAKEID_TYPE_ADD_RESULT);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_message_1
+ * mimics the session buffering a message and delivering it to the transport
+ */
+static void test_message_1(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0;
+ etch_type* msgtype = NULL;
+ etch_value_factory* vf = NULL;
+ const int EXPECTED_BUFSIZE = 4;
+ etch_message* thismessage = NULL;
+
+ /* g_my_transportpacket is the i_transportpacket interface
+ * my_impl_transportpacket is the implementing test class */
+ my_impl_transportpacket* my_transport = g_my_transportpacket->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+
+ vf = (etch_value_factory*) get_current_valuefactory();
+ msgtype = get_vftype(FAKEID_TYPE_ADD);
+
+ g_my_transportpacket->header_size = 0; /* test no header */
+
+ thismessage = new_message(msgtype, ETCH_DEFSIZE, vf);
+
+ /* buffer up the message and deliver to transport */
+ result = g_my_messagizer->transport_message (g_my_messagizer, g_who, thismessage);
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result) break;
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_PACKET);
+ result = is_equal_who(my_transport->recipient, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->buf);
+ CU_ASSERT_EQUAL(my_transport->bufcount, EXPECTED_BUFSIZE);
+
+ /* check that packet buffer contains expected serialized message */
+ CU_ASSERT_EQUAL(my_transport->buf[0], TAGDATA_VERSION);
+ CU_ASSERT_EQUAL(my_transport->buf[1], FAKEID_TYPE_ADD);
+ CU_ASSERT_EQUAL(my_transport->buf[2], 0);
+ CU_ASSERT_EQUAL(my_transport->buf[3], TYPECODE_EOD_MARK);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_message_2
+ * mimics the session buffering a message and delivering it to the transport
+ */
+static void test_message_2(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ { int result = 0, i = 0, errs = 0;
+ etch_type* msgtype = NULL;
+ etch_value_factory* vf = NULL;
+ etch_message* thismessage = NULL;
+ const int TEST_HEADER_SIZE = 8, EXPECTED_BUFSIZE = 4 + TEST_HEADER_SIZE;
+
+ /* g_my_transportpacket is the i_transportpacket interface
+ * my_impl_transportpacket is the implementing test class */
+ my_impl_transportpacket* my_transport = g_my_transportpacket->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+
+ vf = (etch_value_factory*) get_current_valuefactory();
+ msgtype = get_vftype(FAKEID_TYPE_ADD_RESULT);
+
+ g_my_transportpacket->header_size = TEST_HEADER_SIZE; /* test with 8-byte header */
+
+ thismessage = new_message(msgtype, ETCH_DEFSIZE, vf);
+
+ /* buffer up the message and deliver to transport */
+ result = g_my_messagizer->transport_message(g_my_messagizer, g_who, thismessage);
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result) break;
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_PACKET);
+ result = is_equal_who(my_transport->recipient, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->buf);
+ CU_ASSERT_EQUAL(my_transport->bufcount, EXPECTED_BUFSIZE);
+
+ /* check that packet buffer contains expected serialized message */
+ for(; i < TEST_HEADER_SIZE; i++) if (my_transport->buf[i]) errs++;
+ CU_ASSERT_EQUAL(errs, 0);
+ CU_ASSERT_EQUAL(my_transport->buf[8], TAGDATA_VERSION);
+ CU_ASSERT_EQUAL(my_transport->buf[9], FAKEID_TYPE_ADD_RESULT);
+ CU_ASSERT_EQUAL(my_transport->buf[10],0);
+ CU_ASSERT_EQUAL(my_transport->buf[11],TYPECODE_EOD_MARK);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_control
+ * test the session control notification plumbing
+ */
+static void test_session_control(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ {
+ const int MY_CONTROL_CLASSID = 0x5200, MY_VALUE_CLASSID = 0x5201;
+ etch_object* mycontrolobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_CONTROL_CLASSID);
+ etch_object* myvalueobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_VALUE_CLASSID);
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+ CU_ASSERT_EQUAL(my_session->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_session->control);
+ CU_ASSERT_PTR_NULL(my_session->value);
+
+ /* we relinquish memory for mycontrolobj and myvalueobj here.
+ * the session_control terminal destination must destroy them, which here
+ * is handled by our session object destructor when we teardown_this_test() */
+ g_my_messagizer->session_control(g_my_messagizer, (etch_event*)mycontrolobj, myvalueobj);
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_CONTROL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->control);
+ CU_ASSERT_EQUAL(((etch_object*)my_session->control)->class_id, MY_CONTROL_CLASSID);
+ CU_ASSERT_EQUAL(((etch_object*)my_session->value)->class_id, MY_VALUE_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_notify
+ * test the session notify notification plumbing
+ */
+static void test_session_notify(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ {
+ const int MY_EVENT_CLASSID = 0x5202;
+ etch_object* myeventobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_EVENT_CLASSID);
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+ CU_ASSERT_EQUAL(my_session->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_session->eventx);
+
+ /* we relinquish memory for myeventobj here.
+ * the session_control terminal destination must destroy it, which here
+ * is handled by our session object destructor when we teardown_this_test() */
+ g_my_messagizer->session_notify(g_my_messagizer, (etch_event*)myeventobj);
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_NOTIFY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->eventx);
+ CU_ASSERT_EQUAL(((etch_object*)my_session->eventx)->class_id, MY_EVENT_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_query
+ * test the session query notification plumbing
+ */
+static void test_session_query(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ {
+ const int MY_QUERY_CLASSID = 0x5203, MY_RESULT_CLASSID = 0x5204;
+ etch_object* myqueryobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_QUERY_CLASSID);
+ etch_object* myresultobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_RESULT_CLASSID);
+ etch_object* queryresult = NULL;
+
+ /* g_my_sessionmessage is the i_sessionmessage interface
+ * my_impl_sessionmessage is the implementing test class */
+ my_impl_sessionmessage* my_session = g_my_sessionmessage->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+ CU_ASSERT_EQUAL(my_session->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_session->query);
+
+ /* we relinquish myresultobj here. see comments following */
+ my_session->query_result = myresultobj;
+
+ /* we relinquish memory for myqueryobj here and assume queryresult.
+ * the session_control terminal destination must destroy it, which here
+ * is handled by our session object destructor when we teardown_this_test() */
+ queryresult = g_my_messagizer->session_query (g_my_messagizer, (etch_query*)myqueryobj);
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_QUERY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->query);
+ CU_ASSERT_EQUAL(((etch_object*)my_session->query)->class_id, MY_QUERY_CLASSID);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(queryresult);
+ CU_ASSERT_EQUAL(((etch_object*)queryresult)->class_id, MY_RESULT_CLASSID);
+ etch_object_destroy(queryresult);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_control
+ * test the transport control notification plumbing
+ */
+static void test_transport_control(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ {
+ my_impl_transportpacket* my_transport = NULL;
+ const int MY_CONTROL_CLASSID = 0x5200, MY_VALUE_CLASSID = 0x5201;
+ etch_object* mycontrolobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_CONTROL_CLASSID);
+ etch_object* myvalueobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_VALUE_CLASSID);
+
+ /* g_my_transportpacket is the i_transportpacket interface
+ * my_impl_transportpacket is the implementing test class
+ */
+ g_my_messagizer->transport = g_my_transportpacket;
+
+ my_transport = g_my_transportpacket->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+ CU_ASSERT_EQUAL(my_transport->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport->control);
+ CU_ASSERT_PTR_NULL(my_transport->value);
+
+ /* we relinquish memory for mycontrolobj and myvalueobj here.
+ * the transport_control terminal destination must destroy them, which here
+ * is handled by our transport object destructor when we teardown_this_test()
+ */
+ g_my_messagizer->transport_control (g_my_messagizer, (etch_event*)mycontrolobj, myvalueobj);
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_CONTROL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->control);
+ CU_ASSERT_EQUAL(((etch_object*)my_transport->control)->class_id, MY_CONTROL_CLASSID);
+ CU_ASSERT_EQUAL(((etch_object*)my_transport->value)->class_id, MY_VALUE_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_notify
+ * test the transport notify notification plumbing
+ */
+static void test_transport_notify(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ {
+ my_impl_transportpacket* my_transport = NULL;
+ const int MY_EVENT_CLASSID = 0x5202;
+ etch_object* myeventobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_EVENT_CLASSID);
+
+ /* g_my_transportpacket is the i_transportpacket interface
+ * my_impl_transportpacket is the implementing test class
+ */
+ g_my_messagizer->transport = g_my_transportpacket;
+
+ my_transport = g_my_transportpacket->thisx;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+ CU_ASSERT_EQUAL(my_transport->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport->control);
+ CU_ASSERT_PTR_NULL(my_transport->value);
+
+ /* we relinquish memory for myeventobj here.
+ * the transport_control terminal destination must destroy it, which here
+ * is handled by our transport object destructor when we teardown_this_test() */
+ g_my_messagizer->transport_notify (g_my_messagizer, (etch_event*)myeventobj);
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_NOTIFY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->eventx);
+ CU_ASSERT_EQUAL(((etch_object*)my_transport->eventx)->class_id, MY_EVENT_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_query
+ * test the transport query notification plumbing
+ */
+static void test_transport_query(void)
+{
+ setup_this_test(WHICHVF_TESTVF);
+
+ do
+ {
+ my_impl_transportpacket* my_transport = NULL;
+ const int MY_QUERY_CLASSID = 0x5203, MY_RESULT_CLASSID = 0x5204;
+ etch_object* myqueryobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_QUERY_CLASSID);
+ etch_object* myresultobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_RESULT_CLASSID);
+ etch_object* queryresult = NULL;
+
+ /* g_my_transportpacket is the i_transportpacket interface
+ * my_impl_transportpacket is the implementing test class
+ */
+ g_my_messagizer->transport = g_my_transportpacket;
+
+ my_transport = g_my_transportpacket->thisx;
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+ CU_ASSERT_EQUAL(my_transport->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport->control);
+ CU_ASSERT_PTR_NULL(my_transport->value);
+
+ /* we relinquish myresultobj here. see comments following */
+ my_transport->query_result = myresultobj;
+
+ /* we relinquish memory for myqueryobj here and assume queryresult.
+ * the transport_control terminal destination must destroy it, which here
+ * is handled by our transport object destructor when we teardown_this_test() */
+ queryresult = g_my_messagizer->transport_query (g_my_messagizer, (etch_query*)myqueryobj);
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_QUERY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->query);
+ CU_ASSERT_EQUAL(((etch_object*)my_transport->query)->class_id, MY_QUERY_CLASSID);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(queryresult);
+ CU_ASSERT_EQUAL(((etch_object*)queryresult)->class_id, MY_RESULT_CLASSID);
+ etch_object_destroy(queryresult);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_messagizer_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("suite messagizer", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test transportpacket impl constructor", test_transportpacket_constructor);
+ CU_add_test(pSuite, "test sessionmessage impl constructor", test_sessionmessage_constructor);
+ CU_add_test(pSuite, "test messagizer constructor", test_messagizer_constructor);
+ CU_add_test(pSuite, "test test setup and teardown v1", test_testsetup_teardown_a);
+ CU_add_test(pSuite, "test test setup and teardown v2", test_testsetup_teardown_b);
+
+ CU_add_test(pSuite, "test packet 1", test_packet_1);
+ CU_add_test(pSuite, "test packet 2", test_packet_2);
+ CU_add_test(pSuite, "test packet 3", test_packet_3);
+ CU_add_test(pSuite, "test message 1", test_message_1);
+ CU_add_test(pSuite, "test message 2", test_message_2);
+
+ CU_add_test(pSuite, "test session control",test_session_control);
+ CU_add_test(pSuite, "test session notify", test_session_notify);
+ CU_add_test(pSuite, "test session query", test_session_query);
+ CU_add_test(pSuite, "test transport control",test_transport_control);
+ CU_add_test(pSuite, "test transport notify", test_transport_notify);
+ CU_add_test(pSuite, "test transport query", test_transport_query);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/transport/test_packetizer.c b/binding-c/runtime/c/src/test/transport/test_packetizer.c
new file mode 100644
index 0000000..2dd0547
--- /dev/null
+++ b/binding-c/runtime/c/src/test/transport/test_packetizer.c
@@ -0,0 +1,2291 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_packetizer.c
+ */
+#include "etch_runtime.h"
+#include "etch_packetizer.h"
+#include "etch_connection.h"
+#include "etch_nativearray.h"
+#include "etch_encoding.h"
+#include "etch_thread.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - -
+ */
+
+static etch_packetizer* g_my_packetizer;
+static i_sessionpacket* g_my_sessionpacket;
+static i_transportdata* g_my_transportdata;
+static etch_flexbuffer* g_mybuf;
+static etch_who* g_who;
+
+#define THISTEST_WHO_VALUE 0x5151
+static unsigned short CLASSID_MY_IMPL_SP;
+static unsigned short CLASSID_MY_IMPL_TD;
+
+#define is_my_impl_transportdata(x) \
+ (x && ((etch_object*)x)->obj_type == ETCHTYPEB_TRANSPORTDATA && ((etch_object*)x)->class_id == CLASSID_MY_IMPL_TD)
+
+#define is_my_impl_sessionpkt(x) \
+ (x && ((etch_object*)x)->obj_type == ETCHTYPEB_SESSIONPKT&& ((etch_object*)x)->class_id == CLASSID_MY_IMPL_SP)
+
+static int check_packetizer_results(i_transportdata*, etch_nativearray*);
+static int check_packetizer_resultx(i_sessionpacket*, etch_nativearray*);
+static int check_packetized_results (etch_arraylist*, etch_nativearray*);
+static int check_packetized_result (byte* v1, size_t c1, byte* v2, size_t c2);
+static etch_arraylist* new_packetizertest_list();
+
+
+typedef enum etch_what
+{ WHAT_NONE, WHAT_DATA, WHAT_PACKET,
+ TRANSPORT_QUERY, TRANSPORT_CONTROL, TRANSPORT_NOTIFY,
+ SESSION_QUERY, SESSION_CONTROL, SESSION_NOTIFY
+} etch_what;
+
+
+static int is_equal_who(etch_who* who1, etch_who* who2)
+{
+ int n1 = 0, n2 = 0;
+ if (!who1 || !who2) return FALSE;
+ if (((etch_object*)who1)->class_id != CLASSID_WHO || ((etch_object*)who2)->class_id != CLASSID_WHO) return FALSE;
+ if (!who1->value || !who2->value) return FALSE;
+ if (!is_etch_int32(who1->value) || !is_etch_int32(who2->value)) return FALSE;
+ n1 = ((etch_int32*)who1->value)->value;
+ n2 = ((etch_int32*)who2->value)->value;
+ return n1 == n2;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * my_impl_transportdata (i_transportdata implementation)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * my_impl_transportdata
+ * test object implementing i_transportdata
+ */
+typedef struct my_impl_transportdata
+{
+ etch_object object;
+
+ /* i_transportdata interface and methods, plus original destructor
+ * which becomes replaced with a custom destructor to destroy this
+ * object. this is the model for destroying an interface wrapper object
+ * when we do not save and pass around a pointer to the wrapper, but rather
+ * a pointer to the interface. the interface in question, i_transportdata
+ * in this case, contains a pointer to the wrapper object, in this case a
+ * my_impl_transportdata*. when the interface is instantiated, its original
+ * destructor is saved, and is replaced with a destructor which invokes
+ * the wrapper's destructor. the wrapper destructor must then know to
+ * invoke the interface's original destructor when destroying the interface.
+ */
+ i_transportdata* itd; /* owned */
+ etch_object_destructor destroy_transportdata; /* i_transportdata original destructor */
+ etch_transport_data transport_data; /* i_transportdata::transport_data() */
+
+ i_sessiondata* session; /* not owned */
+
+ etch_what what;
+ etch_who* recipient; /* not owned */
+ etch_arraylist* list; /* owned */
+ etch_object* query; /* owned */
+ etch_object* query_result; /* owned */
+ etch_object* control; /* owned */
+ etch_object* value; /* owned */
+ etch_object* eventx; /* owned */
+
+} my_impl_transportdata;
+
+
+/**
+ * destroy_my_impl_transportdata()
+ * my_impl_transportdata destructor
+ */
+static int destroy_my_impl_transportdata (void* data)
+{
+
+ my_impl_transportdata* thisx = (my_impl_transportdata*)data;
+ if (!is_etchobj_static_content(thisx))
+ { /* invoke original i_transportdata destructor */
+ if (thisx->itd && thisx->destroy_transportdata)
+ thisx->destroy_transportdata(thisx->itd);
+
+ etch_object_destroy(thisx->list);
+
+ etch_object_destroy(thisx->query);
+
+ etch_object_destroy(thisx->query_result);
+
+ etch_object_destroy(thisx->control);
+
+ etch_object_destroy(thisx->value);
+
+ etch_object_destroy(thisx->eventx);
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+
+/**
+ * impl_transport_data()
+ * my_impl_transportdata::transport_data
+ * @param whoto caller retains, can be null
+ * @param fbuf caller retains
+ */
+static int impl_transport_data (void* data, void* who, void* buffer)
+{
+ my_impl_transportdata* mytd = (my_impl_transportdata*)data;
+ etch_who* whoto = (etch_who*)who;
+ etch_flexbuffer* fbuf = (etch_flexbuffer*)buffer;
+
+ etch_nativearray* wrapped_array = NULL;
+ byte* newbuf = NULL;
+ size_t bytecount = 0;
+
+ assert(is_my_impl_transportdata(mytd));
+ assert(is_etch_transportdata(mytd->itd));
+
+ mytd->what = WHAT_DATA;
+ mytd->recipient = whoto;
+
+ /* get a new byte vector of the entire packet from the buffer */
+ newbuf = etch_flexbuf_get_all (fbuf, &bytecount);
+
+ /* wrap the packet bytes in an etch_nativearray object */
+ wrapped_array = new_etch_nativearray_from (newbuf,
+ CLASSID_ARRAY_BYTE, sizeof(byte), 1, (int) bytecount, 0, 0);
+
+ /* we want wrapped array to own new packet bytes memory */
+ wrapped_array->is_content_owned = TRUE;
+
+ return etch_arraylist_add (mytd->list, wrapped_array);
+}
+
+
+/**
+ * my_transport_control()
+ * my_impl_transportdata::itransport::transport_control
+ */
+static int my_transport_control (my_impl_transportdata* mytd, etch_object* control, etch_object* value)
+{
+ /* changed parameter from i_transportdata* to my_impl_transportdata to correspond to change
+ * in etch_pktizer_transport_control to pass pzr->transport->thisx rather than pzr->transport.
+ * likewise for my_transport_notify() and my_transport_query. */
+ CU_ASSERT_FATAL(is_my_impl_transportdata(mytd));
+ mytd->what = TRANSPORT_CONTROL;
+ mytd->control = control;
+ mytd->value = value;
+ return 0;
+}
+
+
+/**
+ * my_transport_notify()
+ * my_impl_transportdata::itransport::transport_notify
+ */
+static int my_transport_notify (my_impl_transportdata* mytd, etch_object* evt)
+{
+ CU_ASSERT_FATAL(is_my_impl_transportdata(mytd));
+ mytd->what = TRANSPORT_NOTIFY;
+ mytd->eventx = evt;
+ return 0;
+}
+
+
+/**
+ * my_transport_query()
+ * my_impl_transportdata::itransport::transport_query
+ */
+static etch_object* my_transport_query (my_impl_transportdata* mytd, etch_object* query)
+{
+ etch_object* resultobj = NULL;
+ CU_ASSERT_FATAL(is_my_impl_transportdata(mytd));
+ resultobj = mytd->query_result; /* set artificially in test */
+ mytd->what = TRANSPORT_QUERY;
+ mytd->query = query;
+ mytd->query_result = NULL;
+ return (etch_object*) resultobj; /* caller owns */
+}
+
+
+/**
+ * my_transport_get_session()
+ * my_impl_transportdata::itransport::get_session
+ */
+static i_sessiondata* my_transport_get_session (my_impl_transportdata* mytd)
+{
+ ETCH_ASSERT(is_etch_transportdata(mytd));
+ return mytd->session;
+}
+
+
+/**
+ * my_transport_set_session()
+ * my_impl_transportdata::itransport::set_session
+ */
+static void my_transport_set_session (my_impl_transportdata* mytd, i_sessiondata* newsession)
+{
+ ETCH_ASSERT(is_etch_transportdata(mytd));
+ ETCH_ASSERT(is_etch_sessiondata(newsession));
+ mytd->session = newsession;
+}
+
+
+/*
+ * destroy_my_transportdata()
+ * i_transportdata destructor
+ * this destructor will destroy its parent (my_impl_transportdata),
+ * which will in turn destroy this object.
+ */
+static int destroy_my_transportdata(void* data)
+{
+ i_transportdata* itd = (i_transportdata*)data;
+ my_impl_transportdata* mytd = NULL;
+ if (NULL == itd) return -1;
+
+ mytd = itd->thisx;
+
+ etch_object_destroy(mytd);
+
+ return 0;
+}
+
+
+/**
+ * new_my_impl_transportdata()
+ * my_impl_transportdata constructor
+ */
+static my_impl_transportdata* new_my_impl_transportdata()
+{
+ i_transportdata* itd = NULL;
+ i_transport* itransport = NULL;
+ /* this is a model for dynamic class ID assigment */
+ unsigned short class_id = CLASSID_MY_IMPL_TD? CLASSID_MY_IMPL_TD: (CLASSID_MY_IMPL_TD = get_dynamic_classid());
+
+ my_impl_transportdata* mytd = (my_impl_transportdata*) new_object
+ (sizeof(my_impl_transportdata), ETCHTYPEB_TRANSPORTDATA, class_id);
+
+ ((etch_object*)mytd)->destroy = destroy_my_impl_transportdata;
+
+ itransport = new_transport_interface_ex (mytd,
+ (etch_transport_control) my_transport_control,
+ (etch_transport_notify) my_transport_notify,
+ (etch_transport_query) my_transport_query,
+ (etch_transport_get_session) my_transport_get_session,
+ (etch_transport_set_session) my_transport_set_session);
+
+ itd = new_transportdata_interface(mytd, impl_transport_data, itransport);
+
+ /* save off i_transportdata destructor */
+ mytd->destroy_transportdata = ((etch_object*)itd)->destroy;
+
+ /* replace i_transportdata destructor with one which will destroy this object */
+ ((etch_object*)itd)->destroy = destroy_my_transportdata;
+
+ mytd->list = new_packetizertest_list();
+
+ /* g_my_transportdata will get set to this interface */
+ mytd->itd = itd;
+
+ return mytd;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * my_impl_sessionpacket (i_sessionpacket implementation)
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * my_impl_sessionpacket
+ * test object implementing i_sessionpacket
+ */
+typedef struct my_impl_sessionpacket
+{
+ etch_object object;
+
+ /* i_sessionpacket interface and methods, plus original destructor
+ * which becomes replaced with a custom destructor to destroy this
+ * object. this is the model for destroying an interface wrapper object
+ * when we do not save and pass around a pointer to the wrapper, but rather
+ * a pointer to the interface. the interface in question, i_sessionpacket
+ * in this case, contains a pointer to the wrapper object, in this case a
+ * my_impl_sessionpacket*. when the interface is instantiated, its original
+ * destructor is saved, and is replaced with a destructor which invokes
+ * the wrapper's destructor. the wrapper destructor must then know to
+ * invoke the interface's original destructor when destroying the interface.
+ */
+ i_sessionpacket* isp; /* owned */
+
+ etch_object_destructor destroy_sessionpacket; /* i_sessionpacket original destructor */
+ etch_session_packet session_packet; /* session_packet() */
+
+ etch_what what;
+ etch_who* sender; /* not owned */
+ etch_arraylist* list; /* owned */
+ etch_object* query; /* owned */
+ etch_object* query_result; /* owned */
+ etch_object* control; /* owned */
+ etch_object* value; /* owned */
+ etch_object* eventx; /* owned */
+
+} my_impl_sessionpacket;
+
+
+/**
+ * destroy_my_impl_sessionpacket()
+ * my_impl_sessionpacket destructor
+ */
+static int destroy_my_impl_sessionpacket(void* data)
+{
+
+ my_impl_sessionpacket* thisx = (my_impl_sessionpacket*)data;
+ if (!is_etchobj_static_content(thisx))
+ { /* invoke original i_sessionpacket destructor */
+ if (thisx->isp && thisx->destroy_sessionpacket)
+ thisx->destroy_sessionpacket(thisx->isp);
+
+ etch_object_destroy(thisx->list);
+
+ etch_object_destroy(thisx->query);
+
+ etch_object_destroy(thisx->query_result);
+
+ etch_object_destroy(thisx->control);
+
+ etch_object_destroy(thisx->value);
+
+ etch_object_destroy(thisx->eventx);
+ }
+
+ return destroy_objectex((etch_object*) thisx);
+}
+
+
+/**
+ * impl_session_packet()
+ * my_impl_sessionpacket::session_packet
+ * @param whofrom caller retains, can be null
+ * @param fbuf caller retains
+ */
+static int impl_session_packet (void* data, void* who, void* buffer)
+{
+ my_impl_sessionpacket* mysp = (my_impl_sessionpacket*)data;
+ etch_who* whofrom = (etch_who*)who;
+ etch_flexbuffer* fbuf = (etch_flexbuffer*)buffer;
+
+ etch_nativearray* wrapped_array = NULL;
+ byte* newbuf = NULL;
+ size_t bytecount = 0;
+ CU_ASSERT_FATAL(is_my_impl_sessionpkt(mysp));
+ mysp->what = WHAT_PACKET;
+ mysp->sender = whofrom;
+
+ /* get a new byte vector of the entire packet from the buffer */
+ newbuf = etch_flexbuf_get_all(fbuf, &bytecount);
+
+ /* wrap the packet bytes in an etch_nativearray object */
+ wrapped_array = new_etch_nativearray_from(newbuf,
+ CLASSID_ARRAY_BYTE, sizeof(byte), 1, (int) bytecount, 0, 0);
+
+ /* we want wrapped array to own new packet bytes memory */
+ wrapped_array->is_content_owned = TRUE;
+
+ return etch_arraylist_add(mysp->list, wrapped_array);
+}
+
+
+/**
+ * my_session_control()
+ * my_impl_sessionpacket::isession::session_control
+ */
+static int my_session_control (my_impl_sessionpacket* mysp, etch_object* control, etch_object* value)
+{
+ CU_ASSERT_FATAL(is_my_impl_sessionpkt(mysp));
+ mysp->what = SESSION_CONTROL;
+ mysp->control = control;
+ mysp->value = value;
+ return 0;
+}
+
+
+/**
+ * my_session_notify()
+ * my_impl_sessionpacket::isession::session_notify
+ */
+static int my_session_notify (my_impl_sessionpacket* mysp, etch_object* evt)
+{
+ CU_ASSERT_FATAL(is_my_impl_sessionpkt(mysp));
+ mysp->what = SESSION_NOTIFY;
+ mysp->eventx = evt;
+ return 0;
+}
+
+
+/**
+ * my_session_query()
+ * my_impl_sessionpacket::isession::session_query
+ */
+static etch_object* my_session_query (my_impl_sessionpacket* mysp, etch_object* query)
+{
+ etch_object* resultobj = NULL;
+ CU_ASSERT_FATAL(is_my_impl_sessionpkt(mysp));
+ resultobj = mysp->query_result; /* set artificially in test */
+ mysp->what = SESSION_QUERY;
+ mysp->query = query;
+ mysp->query_result = NULL;
+ return (etch_object*) resultobj; /* caller owns */
+}
+
+
+/**
+ * new_packetizertest_list()
+ * constructor for a list which owns content memory and where content is etch_object derived.
+ * content will be etch_nativearray objects
+ */
+static etch_arraylist* new_packetizertest_list()
+{
+ etch_arraylist* list = new_etch_arraylist(32,0);
+ list->content_type = ETCHARRAYLIST_CONTENT_OBJECT; /* list can call destroy() on list item */
+ list->is_readonly = FALSE;
+ return list;
+}
+
+
+/*
+ * destroy_my_sessionpacket()
+ * i_sessionpacket destructor
+ * this destructor will destroy its parent (my_impl_sessionpacket),
+ * which will in turn destroy this object.
+ */
+static int destroy_my_sessionpacket(void* data)
+{
+ i_sessionpacket* itp = (i_sessionpacket*)data;
+ my_impl_sessionpacket* mytp = NULL;
+ if (NULL == itp) return -1;
+
+ mytp = itp->thisx;
+
+ etch_object_destroy(mytp);
+
+ return 0;
+}
+
+
+/**
+ * new_my_impl_sessionpacket()
+ * my_impl_sessionpacket constructor
+ */
+static my_impl_sessionpacket* new_my_impl_sessionpacket()
+{
+ i_sessionpacket* isessionpkt = NULL;
+ i_session* isession = NULL;
+ /* this is a model for dynamic class ID assigment */
+ unsigned short class_id = CLASSID_MY_IMPL_SP? CLASSID_MY_IMPL_SP:
+ (CLASSID_MY_IMPL_SP = get_dynamic_classid());
+
+ my_impl_sessionpacket* mysessionpkt = (my_impl_sessionpacket*) new_object
+ (sizeof(my_impl_sessionpacket), ETCHTYPEB_SESSIONPKT, class_id);
+
+ ((etch_object*)mysessionpkt)->destroy = destroy_my_impl_sessionpacket;
+
+ mysessionpkt->list = new_packetizertest_list();
+
+ isession = new_session_interface(mysessionpkt,
+ (etch_session_control) my_session_control,
+ (etch_session_notify) my_session_notify,
+ (etch_session_query) my_session_query);
+
+ isessionpkt = new_sessionpkt_interface(mysessionpkt, impl_session_packet, isession);
+
+ /* save off i_sessionpacket destructor */
+ mysessionpkt->destroy_sessionpacket = ((etch_object*)isessionpkt)->destroy;
+
+ /* replace i_sessionpacket destructor with one which will destroy this object */
+ ((etch_object*)isessionpkt)->destroy = destroy_my_sessionpacket;
+
+ /* g_my_sessionpacket will get set to this interface */
+ mysessionpkt->isp = isessionpkt;
+
+ return mysessionpkt;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * support functions
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * check_packetizer_results()
+ * check array of packetizer result byte arrays against a control byte array.
+ * arrays may be packet payloads, or packets with headers, depending on test.
+ * result arrays are found in the packet handler arraylist, each such list item
+ * being an etch_nativearray object of one dimension, wrapping that result array.
+ * control arrays are passed as an etch_nativearray of two dimensions, created
+ * from a static 2-dimensional array.
+ */
+static int check_packetizer_results(i_transportdata* itd, etch_nativearray* expected_array)
+{
+ my_impl_transportdata* mytd = (my_impl_transportdata*) itd->thisx;
+ const int packetcount = mytd->list->count; /* # of result arrays in list */
+ int errors = 0;
+
+ if (NULL == expected_array) /* todo: also check for zero populated count */
+ return packetcount == 0? 0: -1;
+
+ if (packetcount != expected_array->dimension[1])
+ return -1; /* check against count of expected results */
+
+ errors = check_packetized_results(mytd->list, expected_array);
+
+ return errors? -1: 0;
+}
+
+
+/**
+ * check_packetizer_resultx()
+ * check array of packetizer result byte arrays against a control byte array.
+ * arrays may be packet payloads, or packets with headers, depending on test.
+ * result arrays are found in the packet handler arraylist, each such list item
+ * being an etch_nativearray object of one dimension, wrapping that result array.
+ * control arrays are passed as an etch_nativearray of two dimensions, created
+ * from a static 2-dimensional array.
+ */
+static int check_packetizer_resultx(i_sessionpacket* isp, etch_nativearray* expected_array)
+{
+ my_impl_sessionpacket* mysp = (my_impl_sessionpacket*) isp->thisx;
+ const int packetcount = mysp->list->count; /* # of result arrays in list */
+ int errors = 0;
+
+ if (NULL == expected_array) /* todo: also check for zero populated count */
+ return packetcount == 0? 0: -1;
+
+ if (packetcount != expected_array->dimension[1])
+ return -1; /* check against count of expected results */
+
+ errors = check_packetized_results(mysp->list, expected_array);
+
+ return errors? -1: 0;
+}
+
+
+/**
+ * check_packetized_results()
+ */
+static int check_packetized_results(etch_arraylist* list, etch_nativearray* expected_array)
+{
+ etch_iterator iterator;
+ int result = 0, errors = 0, i = 0;
+ set_iterator(&iterator, list, &list->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ /* get result (one-dimension byte array) out of list */
+ etch_nativearray* resultobj = etch_arraylist_get(list, i);
+ byte* packetizedbytes = resultobj->values;
+ size_t packetizedbytecount = resultobj->dimension[0];
+
+ /* get (offset to) this result out of expected results */
+ size_t dim2offset = etch_nativearray_off2(expected_array, i, 0);
+ byte* expected_bytes = (byte*) expected_array->values + dim2offset;
+ size_t expected_bytecount = expected_array->dimension[0];
+
+ result = check_packetized_result(packetizedbytes, packetizedbytecount,
+ expected_bytes, expected_bytecount);
+
+ if (result == -1)
+ errors++;
+
+ i++;
+ iterator.next(&iterator);
+ }
+
+ return errors;
+}
+
+
+/**
+ * check_packetized_result()
+ * check a single packetizer result array against a control array,
+ * each represented as a byte vector. the array can be a packet, or a packet payload,
+ * depending on tested direction of the packetizer call.
+ */
+static int check_packetized_result (byte* v1, size_t c1, byte* v2, size_t c2)
+{
+ byte *p = 0, *q = 0;
+ size_t i = 0, errors = 0;
+
+ if (c1 != c2) return FALSE;
+ if (!v1 && !v2) return TRUE;
+ if (!v1 || !v2) return FALSE;
+
+ for(p = v1, q = v2; i < c1; i++, p++, q++)
+ if (*p != *q)
+ errors++;
+
+ return errors? -1: 0;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * individual setup and teardown
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * setup_this_test()
+ */
+static int setup_this_test()
+{
+ my_impl_transportdata* mytd_impl = NULL;
+ my_impl_sessionpacket* mysp_impl = NULL;
+
+ /* we instantiate a wrapper x which implements and instantiates i_transportpacket.
+ * the instantiation of i_transportpacket will contain a pointer to x.
+ * our global reference g_my_transportpacket is a pointer to the interface.
+ * the purpose of this excercise is that, in the real binding we can pass
+ * around the interface, whose methods can be then invoked without knowing
+ * anything about the wrapper. when we want to reference the wrapper x,
+ * it is (my_impl_transportpacket) g_my_transportpacket->thisx.
+ */
+ mytd_impl = new_my_impl_transportdata();
+ g_my_transportdata = mytd_impl->itd;
+ CU_ASSERT_FATAL(is_my_impl_transportdata(g_my_transportdata->thisx));
+
+ /* we instantiate a wrapper y which implements and instantiates i_sessionpacket.
+ * the instantiation of i_sessionpacket will contain a pointer to y.
+ * our global reference g_my_sessionpacket is a pointer to the interface.
+ * the purpose of this excercise is that, in the real binding we can pass
+ * around the interface, whose methods can be then invoked without knowing
+ * anything about the wrapper. when we want to reference the wrapper x,
+ * it is (my_impl_sessionpacket*) g_my_sessionpacket->thisx.
+ */
+ mysp_impl = new_my_impl_sessionpacket();
+ g_my_sessionpacket = mysp_impl->isp;
+
+ g_who = new_who(new_int32(THISTEST_WHO_VALUE));
+
+ /* finally instantiate the test packetizer */
+ g_my_packetizer = new_packetizer(g_my_transportdata, L"tcp", NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_my_packetizer);
+ g_my_packetizer->set_session(g_my_packetizer, g_my_sessionpacket);
+
+ return 0;
+}
+
+
+/**
+ * teardown_this_test()
+ */
+static int teardown_this_test()
+{
+ etch_object_destroy(g_my_packetizer);
+
+ etch_object_destroy(g_my_sessionpacket); // destroy() is now 0xfeeefeee
+
+ etch_object_destroy(g_my_transportdata);
+
+ etch_object_destroy(g_who);
+
+ etch_object_destroy(g_mybuf);
+
+ g_my_packetizer = NULL;
+ g_my_sessionpacket = NULL;
+ g_my_transportdata = NULL;
+ g_mybuf = NULL;
+ g_who = NULL;
+
+ return 0;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - -
+ * tests
+ * - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_sessionpacket_constructor()
+ */
+static void test_sessionpacket_constructor(void)
+{
+ my_impl_sessionpacket* mysp_impl = new_my_impl_sessionpacket();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mysp_impl);
+
+ /* the custom interface object destructors are coded to destroy their
+ * implementing objects, so we do that here to verify this functionality.
+ */
+ do
+ { i_sessionpacket* isp = mysp_impl->isp;
+ etch_object_destroy(isp);
+
+ } while(0);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transportdata_constructor()
+ */
+static void test_transportdata_constructor(void)
+{
+ my_impl_transportdata* mytd_impl = new_my_impl_transportdata();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mytd_impl);
+
+ /* the custom interface object destructors are coded to destroy their
+ * implementing objects, so we do that here to verify this functionality.
+ */
+ do
+ { i_transportdata* itd = mytd_impl->itd;
+ etch_object_destroy(itd);
+
+ } while(0);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_packetizer_constructor()
+ */
+static void test_packetizer_constructor(void)
+{
+ etch_packetizer* pzr = NULL;
+ i_transportdata* itd = NULL;
+ my_impl_transportdata* mytd_impl = NULL;
+
+ mytd_impl = new_my_impl_transportdata();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mytd_impl);
+
+ itd = mytd_impl->itd;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(itd);
+
+ /* packetizer does not own i_transportdata* itd */
+ pzr = new_packetizer(itd, L"tcp://127.0.0.1:4001?Packetizer.maxPktSize=110480", NULL);
+ CU_ASSERT_TRUE(pzr->maxpacketsize == 110480);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(pzr);
+
+ etch_object_destroy(pzr);
+
+ /* i_transportdata.destroy() will destroy my_impl_transportdata */
+ etch_object_destroy(itd);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_test_setup()
+ * test that the test setup works with all memory accounted for.
+ * if this test fails, all subsequent tests would also fail.
+ * all subsequent tests should include this skeleton code.
+ */
+static void test_test_setup(void)
+{
+ int result = setup_this_test();
+
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_who);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_my_packetizer);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_my_sessionpacket);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_my_transportdata);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_to_source_packet1()
+ * test 1 for packets delivered to packet source
+ */
+static void test_to_source_packet1(void)
+{
+ setup_this_test();
+
+ do
+ {
+ int result = 0;
+ byte* disposable_packet = NULL;
+ #define EXPECTED_HEADERSIZE 8
+ etch_nativearray* expected_result_array = NULL;
+ my_impl_transportdata* mytransport = g_my_transportdata->thisx;
+
+ byte empty_packet[EXPECTED_HEADERSIZE] = { 0,0,0,0, 0,0,0,0, };
+
+ byte expected_result[][EXPECTED_HEADERSIZE] =
+ {
+ {
+ -34,-83,-66,-17, 0,0,0,0,
+ },
+ };
+
+ CU_ASSERT_EQUAL_FATAL(g_my_packetizer->headersize, EXPECTED_HEADERSIZE);
+
+ expected_result_array = new_etch_nativearray_from
+ (&expected_result, CLASSID_ARRAY_BYTE, sizeof(byte), 2, EXPECTED_HEADERSIZE, 1, 0);
+
+ disposable_packet = etch_malloc(sizeof(empty_packet), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, empty_packet, sizeof(empty_packet));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(empty_packet), 8, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+
+ result = g_my_packetizer->transport_packet(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = check_packetizer_results(g_my_packetizer->transport, expected_result_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mytransport->what, WHAT_DATA);
+ result = is_equal_who(mytransport->recipient, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ etch_object_destroy(expected_result_array);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_to_source_packet2()
+ * test 2 for packets delivered to packet source
+ */
+static void test_to_source_packet2(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TTSP2BYTECOUNT 9
+ byte* disposable_packet = NULL;
+ etch_nativearray* expected_result_array = NULL;
+ my_impl_transportdata* mytransport = g_my_transportdata->thisx;
+
+ byte test_packet[TTSP2BYTECOUNT] = { 0,0,0,0, 0,0,0,0, 1 };
+
+ byte expected_result[][TTSP2BYTECOUNT] =
+ { /* --signature--- data-length data */
+ { /* de ad be ef 00 00 00 01 01 */
+ -34,-83,-66,-17, 0, 0, 0, 1, 1
+ },
+ };
+
+ CU_ASSERT_EQUAL_FATAL(g_my_packetizer->headersize, EXPECTED_HEADERSIZE);
+
+ expected_result_array = new_etch_nativearray_from
+ (&expected_result, CLASSID_ARRAY_BYTE, sizeof(byte), 2, TTSP2BYTECOUNT, 1, 0);
+
+ disposable_packet = etch_malloc(sizeof(test_packet), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet, sizeof(test_packet));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet), 9, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+
+ result = g_my_packetizer->transport_packet(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = check_packetizer_results(g_my_packetizer->transport, expected_result_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mytransport->what, WHAT_DATA);
+ result = is_equal_who(mytransport->recipient, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ etch_object_destroy(expected_result_array);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_to_source_packet3()
+ * test 3 for packets delivered to packet source
+ */
+static void test_to_source_packet3(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TTSP3BYTECOUNT 10
+ byte* disposable_packet = NULL;
+ etch_nativearray* expected_result_array = NULL;
+ my_impl_transportdata* mytransport = g_my_transportdata->thisx;
+
+ byte test_packet[TTSP3BYTECOUNT] = { 0,0,0,0, 0,0,0,0, 2, 3 };
+
+ byte expected_result[][TTSP3BYTECOUNT] =
+ { /* --signature--- data-length data */
+ { /* de ad be ef 00 00 00 02 02 03 */
+ -34,-83,-66,-17, 0, 0, 0, 2, 2, 3
+ },
+ };
+
+ CU_ASSERT_EQUAL_FATAL(g_my_packetizer->headersize, EXPECTED_HEADERSIZE);
+
+ expected_result_array = new_etch_nativearray_from
+ (&expected_result, CLASSID_ARRAY_BYTE, sizeof(byte), 2, TTSP3BYTECOUNT, 1, 0);
+
+ disposable_packet = etch_malloc(sizeof(test_packet), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet, sizeof(test_packet));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet), 10, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+
+ result = g_my_packetizer->transport_packet(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = check_packetizer_results(g_my_packetizer->transport, expected_result_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mytransport->what, WHAT_DATA);
+ result = is_equal_who(mytransport->recipient, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ etch_object_destroy(expected_result_array);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_to_source_badpacket1()
+ * test 4 for packets delivered to packet source
+ */
+static void test_to_source_badpacket1(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TTSP4BYTECOUNT 7
+ byte* disposable_packet = NULL;
+
+ byte test_packet[TTSP4BYTECOUNT] = { 0,0,0,0, 0,0,0 }; /* too short */
+
+ disposable_packet = etch_malloc(sizeof(test_packet), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet, sizeof(test_packet));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet), 256, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+
+ result = g_my_packetizer->transport_packet(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result, -1);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_badpacket1()
+ * test 1 for bad packet data
+ */
+static void test_badpacket1(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TBP1COUNT 8
+ byte* disposable_packet = NULL;
+ byte test_packet_1[TBP1COUNT] = { 0,0,0,0, 0,0,0,0 }; /* bad sig */
+ byte test_packet_2[TBP1COUNT] = { 1,2,3,4, 0,0,0,0 }; /* bad sig */
+ byte test_packet_3[TBP1COUNT] = { -34,-83,-66,-17, 0,1,0,0 }; /* bad len */
+
+ /* bad signature 1 */
+ disposable_packet = etch_malloc(sizeof(test_packet_1), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet_1, sizeof(test_packet_1));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet_1), 256, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result, -1);
+
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+
+ /* bad signature 2 */
+ disposable_packet = etch_malloc(sizeof(test_packet_2), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet_2, sizeof(test_packet_2));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet_2), 256, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result, -1);
+
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+
+ /* bad data length (65536) */
+ disposable_packet = etch_malloc(sizeof(test_packet_3), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet_3, sizeof(test_packet_3));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet_3), 256, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result, -1);
+
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_single_single_data()
+ * 3 tests for single packet in a single buffer
+ */
+static void test_single_single_data(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TSPSB1BYTECOUNT 8
+ #define TSPSB2BYTECOUNT 9
+ #define TSPSB2RESULTBYTECOUNT 1
+ #define TSPSB3BYTECOUNT 10
+ #define TSPSB3RESULTBYTECOUNT 2
+ byte* disposable_packet = NULL;
+ etch_nativearray* expected_result_2_array = NULL;
+ etch_nativearray* expected_result_3_array = NULL;
+
+ my_impl_sessionpacket* mysessionpkt = g_my_sessionpacket->thisx;
+
+ byte test_packet_1[TSPSB1BYTECOUNT] = { -34,-83,-66,-17, 0,0,0,0, };
+ byte test_packet_2[TSPSB2BYTECOUNT] = { -34,-83,-66,-17, 0,0,0,1, 1, };
+ byte test_packet_3[TSPSB3BYTECOUNT] = { -34,-83,-66,-17, 0,0,0,2, 3,4 };
+
+ byte expected_result_2[][TSPSB2RESULTBYTECOUNT] =
+ {
+ {
+ 1
+ },
+ };
+
+ byte expected_result_3[][TSPSB3RESULTBYTECOUNT] =
+ {
+ {
+ 3, 4
+ },
+ };
+
+ /* test 1 */
+ disposable_packet = etch_malloc(sizeof(test_packet_1), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet_1, sizeof(test_packet_1));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet_1), 64, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(test_packet_1));
+
+ result = g_my_packetizer->session_data (g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = check_packetizer_resultx(g_my_sessionpacket, NULL);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(mysessionpkt->sender);
+
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+
+ /* test 2 */
+ disposable_packet = etch_malloc(sizeof(test_packet_2), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet_2, sizeof(test_packet_2));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet_2), 9, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(test_packet_2));
+
+ result = g_my_packetizer->session_data (g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ expected_result_2_array = new_etch_nativearray_from
+ (&expected_result_2, CLASSID_ARRAY_BYTE, sizeof(byte), 2, TSPSB2RESULTBYTECOUNT, 1, 0);
+
+ result = check_packetizer_resultx (g_my_sessionpacket, expected_result_2_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_PACKET);
+ result = is_equal_who(mysessionpkt->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ etch_object_destroy(expected_result_2_array);
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+ etch_arraylist_clear (mysessionpkt->list, TRUE);
+
+ /* test 3 */
+ disposable_packet = etch_malloc(sizeof(test_packet_3), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet_3, sizeof(test_packet_3));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet_3), 10, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(test_packet_3));
+
+ result = g_my_packetizer->session_data (g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ expected_result_3_array = new_etch_nativearray_from
+ (&expected_result_3, CLASSID_ARRAY_BYTE, sizeof(byte), 2, TSPSB3RESULTBYTECOUNT, 1, 0);
+
+ result = check_packetizer_resultx (g_my_sessionpacket, expected_result_3_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_PACKET);
+ result = is_equal_who(mysessionpkt->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ etch_object_destroy(expected_result_3_array);
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_two_in_one_buffer_data_1()
+ * test two packets in a single buffer
+ */
+static void test_two_in_one_buffer_data_1(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TWOIN1_1_BYTECOUNT (8 + 8)
+ signed char* disposable_packet;
+
+ signed char test_packet[TWOIN1_1_BYTECOUNT]
+ = { -34,-83,-66,-17, 0,0,0,0,
+ -34,-83,-66,-17, 0,0,0,0,
+ };
+
+ my_impl_sessionpacket* mysessionpkt = g_my_sessionpacket->thisx;
+ disposable_packet = etch_malloc(sizeof(test_packet), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet, sizeof(test_packet));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet), 64, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(test_packet));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = check_packetizer_resultx(g_my_sessionpacket, NULL);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(mysessionpkt->sender);
+
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_two_in_one_buffer_data_2()
+ * test two packets in a single buffer - 2
+ */
+static void test_two_in_one_buffer_data_2(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TWOIN1_2_BYTECOUNT (8 + 1 + 8)
+ #define TWOIN1_2_RESULT_BYTECOUNT 1
+ byte* disposable_packet;
+
+ byte test_packet[TWOIN1_2_BYTECOUNT]
+ = { -34,-83,-66,-17, 0,0,0,1, 1,
+ -34,-83,-66,-17, 0,0,0,0,
+ };
+
+ byte expected_result[][TWOIN1_2_RESULT_BYTECOUNT] =
+ {
+ {
+ 1
+ },
+ };
+
+ my_impl_sessionpacket* mysessionpkt = g_my_sessionpacket->thisx;
+
+ etch_nativearray* expected_result_array = new_etch_nativearray_from
+ (&expected_result, CLASSID_ARRAY_BYTE, sizeof(byte), 2,
+ TWOIN1_2_RESULT_BYTECOUNT, 1, 0);
+
+ disposable_packet = etch_malloc(sizeof(test_packet), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet, sizeof(test_packet));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet), 17, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(test_packet));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = check_packetizer_resultx(g_my_sessionpacket, expected_result_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_PACKET);
+ result = is_equal_who(mysessionpkt->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+ etch_object_destroy(expected_result_array);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_two_in_one_buffer_data_3()
+ * test two packets in a single buffer - 3
+ */
+static void test_two_in_one_buffer_data_3(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TWOIN1_3_BYTECOUNT (8 + 8 + 1)
+ #define TWOIN1_3_RESULT_BYTECOUNT 1
+ byte* disposable_packet;
+
+ byte test_packet[TWOIN1_3_BYTECOUNT]
+ = { -34,-83,-66,-17, 0,0,0,0,
+ -34,-83,-66,-17, 0,0,0,1, 2
+ };
+
+ byte expected_result[][TWOIN1_3_RESULT_BYTECOUNT] =
+ {
+ {
+ 2
+ },
+ };
+
+ my_impl_sessionpacket* mysessionpkt = g_my_sessionpacket->thisx;
+
+ etch_nativearray* expected_result_array = new_etch_nativearray_from
+ (&expected_result, CLASSID_ARRAY_BYTE, sizeof(byte), 2,
+ TWOIN1_3_RESULT_BYTECOUNT, 1, 0);
+
+ disposable_packet = etch_malloc(sizeof(test_packet), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet, sizeof(test_packet));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet), 17, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(test_packet));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = check_packetizer_resultx(g_my_sessionpacket, expected_result_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_PACKET);
+ result = is_equal_who(mysessionpkt->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+ etch_object_destroy(expected_result_array);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_two_in_one_buffer_data_4()
+ * test two packets in a single buffer - 4
+ */
+static void test_two_in_one_buffer_data_4(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ signed char* disposable_packet;
+
+ /* byte count for all packets */
+ #define TWOIN1_4_BYTECOUNT (8 + 1 + 8 + 1)
+
+ /* byte count for low order dimension of expected result array ( [2][1] ) */
+ #define TWOIN1_4_RESULT_BYTECOUNT 1
+
+ signed char test_packet[TWOIN1_4_BYTECOUNT]
+ = { -34,-83,-66,-17, 0,0,0,1, 1,
+ -34,-83,-66,-17, 0,0,0,1, 2,
+ };
+
+ signed char expected_result[][TWOIN1_4_RESULT_BYTECOUNT] =
+ {
+ {
+ 1, /* packet 1 data */
+ },
+ {
+ 2, /* packet 2 data */
+ },
+ };
+
+ my_impl_sessionpacket* mysessionpkt = g_my_sessionpacket->thisx;
+
+ etch_nativearray* expected_result_array = new_etch_nativearray_from
+ (&expected_result, CLASSID_ARRAY_BYTE,
+ sizeof(byte), /* size of an array item is 1 */
+ 2, /* count of dimensions [2][1] */
+ 1, /* count of items in low order dimension */
+ 2, /* count of items in secondary dimension */
+ 0); /* 3rd dimension (not applicable) */
+
+ disposable_packet = etch_malloc(sizeof(test_packet), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet, sizeof(test_packet));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet), 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(test_packet));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = check_packetizer_resultx(g_my_sessionpacket, expected_result_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_PACKET);
+ result = is_equal_who(mysessionpkt->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+ etch_object_destroy(expected_result_array);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_two_in_one_buffer_data_5()
+ * test two packets in a single buffer - 5
+ */
+static void test_two_in_one_buffer_data_5(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ signed char* disposable_packet;
+
+ /* byte count for all packets */
+ #define TWOIN1_5_BYTECOUNT (8 + 2 + 8 + 2)
+
+ /* byte count for low order dimension of expected result array ( [2][2] ) */
+ #define TWOIN1_5_RESULT_BYTECOUNT 2
+
+ signed char test_packet[TWOIN1_5_BYTECOUNT]
+ = { -34,-83,-66,-17, 0,0,0,2, 3, 4,
+ -34,-83,-66,-17, 0,0,0,2, 5, 6,
+ };
+
+ signed char expected_result[][TWOIN1_5_RESULT_BYTECOUNT] =
+ {
+ {
+ 3, 4, /* packet 1 data */
+ },
+ {
+ 5, 6, /* packet 2 data */
+ },
+ };
+
+ my_impl_sessionpacket* mysessionpkt = g_my_sessionpacket->thisx;
+
+ etch_nativearray* expected_result_array = new_etch_nativearray_from
+ (&expected_result, CLASSID_ARRAY_BYTE,
+ sizeof(byte), /* size of an array item is 1 */
+ 2, /* count of dimensions [2][2] */
+ 2, /* count of items in low order dimension */
+ 2, /* count of items in secondary dimension */
+ 0); /* 3rd dimension (not applicable) */
+
+ disposable_packet = etch_malloc(sizeof(test_packet), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, test_packet, sizeof(test_packet));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(test_packet), 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(test_packet));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = check_packetizer_resultx(g_my_sessionpacket, expected_result_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_PACKET);
+ result = is_equal_who(mysessionpkt->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+ etch_object_destroy(expected_result_array);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_two_in_two_buffers_data_1()
+ * test two packets in two buffers with header split across buffers
+ * packets are empty in this test
+ */
+static void test_two_in_two_buffers_data_1(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TWOIN2_1_BUF1_BYTECOUNT 6
+ #define TWOIN2_1_BUF2_BYTECOUNT 10
+ etch_flexbuffer* mybuf2=0;
+ byte* disposable_packet=0;
+
+ byte buf1_data[TWOIN2_1_BUF1_BYTECOUNT]
+ = { -34,-83,-66,-17, 0,0, /* packet 1 header (partial) */
+ };
+
+ byte buf2_data[TWOIN2_1_BUF2_BYTECOUNT]
+ = { 0, 0, /* packet 1 header (balance) */
+ -34,-83,-66,-17, 0,0,0,0, /* packet 2 header */
+ };
+
+ my_impl_sessionpacket* mysessionpkt = g_my_sessionpacket->thisx;
+
+ /* buffer 1 */
+ disposable_packet = etch_malloc(sizeof(buf1_data), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, buf1_data, sizeof(buf1_data));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(buf1_data), 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(buf1_data));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(mysessionpkt->sender);
+
+ /* buffer 2 */
+ disposable_packet = etch_malloc(sizeof(buf2_data), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, buf2_data, sizeof(buf2_data));
+
+ mybuf2 = new_flexbuffer_from(disposable_packet, sizeof(buf2_data), 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mybuf2);
+ CU_ASSERT_EQUAL(mybuf2->datalen, sizeof(buf2_data));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(mysessionpkt->sender);
+
+ /* done - free memory */
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+ etch_object_destroy(mybuf2);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_two_in_two_buffers_data_2()
+ * test two packets in two buffers with header split across buffers
+ * packets include bodies in this test
+ */
+static void test_two_in_two_buffers_data_2(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TWOIN2_2_BUF1_BYTECOUNT 6
+ #define TWOIN2_2_BUF2_BYTECOUNT (2 + 2 + 8 + 2)
+ /* byte count for low order dimension of expected result array ( [2][2] ) */
+ #define TWOIN2_2_RESULT_BYTECOUNT 2
+ etch_flexbuffer* mybuf2=0;
+ byte* disposable_packet=0;
+
+ byte buf1_data[TWOIN2_2_BUF1_BYTECOUNT]
+ = { -34,-83,-66,-17, 0,0, /* packet 1 header (partial) */
+ };
+
+ byte buf2_data[TWOIN2_2_BUF2_BYTECOUNT]
+ = { 0, 2, /* packet 1 header (balance) */
+ 3, 4, /* packet 1 body */
+ -34,-83,-66,-17, 0,0,0,2, /* packet 2 header */
+ 5, 6, /* packet 2 body */
+ };
+
+ byte expected_result[][TWOIN2_2_RESULT_BYTECOUNT] =
+ {
+ {
+ 3, 4, /* packet 1 body */
+ },
+ {
+ 5, 6, /* packet 2 body */
+ },
+ };
+
+ my_impl_sessionpacket* mysessionpkt = g_my_sessionpacket->thisx;
+
+ etch_nativearray* expected_result_array = new_etch_nativearray_from
+ (&expected_result, CLASSID_ARRAY_BYTE,
+ sizeof(byte), /* size of an array item is 1 */
+ 2, /* count of dimensions [2][2] */
+ 2, /* count of items in low order dimension */
+ 2, /* count of items in secondary dimension */
+ 0 /* 3rd dimension (not applicable) */
+ );
+
+ /* buffer 1 */
+ disposable_packet = etch_malloc(sizeof(buf1_data), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, buf1_data, sizeof(buf1_data));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(buf1_data), 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(buf1_data));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(mysessionpkt->sender);
+
+ /* buffer 2 */
+ disposable_packet = etch_malloc(sizeof(buf2_data), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, buf2_data, sizeof(buf2_data));
+
+ mybuf2 = new_flexbuffer_from(disposable_packet, sizeof(buf2_data), 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mybuf2);
+ CU_ASSERT_EQUAL(mybuf2->datalen, sizeof(buf2_data));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, mybuf2);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = check_packetizer_resultx(g_my_sessionpacket, expected_result_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_PACKET);
+ result = is_equal_who(mysessionpkt->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ /* done - free memory */
+ etch_object_destroy(expected_result_array);
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+ etch_object_destroy(mybuf2);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_two_in_two_buffers_data_3()
+ * test two packets in two buffers with body split across buffers
+ */
+static void test_two_in_two_buffers_data_3(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TWOIN2_3_BUF1_BYTECOUNT (8 + 1) /* header1 + body1 1 of 2 */
+ #define TWOIN2_3_BUF2_BYTECOUNT (1 + 8 + 2) /* body1 2 of 2 + header2 + body2 */
+ /* byte count for low order dimension of expected result array ( [2][2] ) */
+ #define TWOIN2_3_RESULT_BYTECOUNT 2
+ etch_flexbuffer* mybuf2 = NULL;
+ signed char* disposable_packet = NULL;
+
+ signed char buf1_data[TWOIN2_3_BUF1_BYTECOUNT]
+ = { -34,-83,-66,-17, 0, 0, 0, 2, /* packet 1 header (length 2) */
+ 1, /* packet 1 partial body */
+ };
+
+ signed char buf2_data[TWOIN2_3_BUF2_BYTECOUNT]
+ = { 2, /* packet 1 body balance */
+ -34,-83,-66,-17, 0, 0, 0, 2, /* packet 2 header */
+ 3, 4, /* packet 2 body */
+ };
+
+ signed char expected_result[][TWOIN2_3_RESULT_BYTECOUNT] =
+ {
+ {
+ 1, 2, /* packet 1 body */
+ },
+ {
+ 3, 4, /* packet 2 body */
+ },
+ };
+
+ my_impl_sessionpacket* mysessionpkt = g_my_sessionpacket->thisx;
+
+ etch_nativearray* expected_result_array = new_etch_nativearray_from
+ (&expected_result, CLASSID_ARRAY_BYTE,
+ sizeof(byte), /* size of an array item is 1 */
+ 2, /* count of dimensions [2][2] */
+ 2, /* count of items in low order dimension */
+ 2, /* count of items in secondary dimension */
+ 0 /* 3rd dimension (not applicable) */
+ );
+
+ /* buffer 1 */
+ disposable_packet = etch_malloc(sizeof(buf1_data), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, buf1_data, sizeof(buf1_data));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(buf1_data), 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(buf1_data));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(mysessionpkt->sender);
+
+ /* buffer 2 */
+ disposable_packet = etch_malloc(sizeof(buf2_data), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, buf2_data, sizeof(buf2_data));
+
+ mybuf2 = new_flexbuffer_from(disposable_packet, sizeof(buf2_data), 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mybuf2);
+ CU_ASSERT_EQUAL(mybuf2->datalen, sizeof(buf2_data));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, mybuf2);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = check_packetizer_resultx(g_my_sessionpacket, expected_result_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_PACKET);
+ result = is_equal_who(mysessionpkt->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+
+ /* done - free memory */
+ etch_object_destroy(expected_result_array);
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+ etch_object_destroy(mybuf2);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_two_in_two_buffers_data_4()
+ * test two packets in two buffers with body split across buffers
+ */
+static void test_two_in_two_buffers_data_4(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ #define TWOIN2_4_BUF1_BYTECOUNT (8 + 2) /* header1 + body1 2 of 3 */
+ #define TWOIN2_4_BUF2_BYTECOUNT (1 + 8 + 3) /* body1 3 of 3 + header2 + body2 */
+ /* byte count for low order dimension of expected result array ( [2][2] ) */
+ #define TWOIN2_4_RESULT_BYTECOUNT 3
+ etch_flexbuffer* mybuf2=0;
+ byte* disposable_packet=0;
+
+ byte buf1_data[TWOIN2_4_BUF1_BYTECOUNT]
+ = { -34,-83,-66,-17, 0, 0, 0, 3, /* packet 1 header (length 2) */
+ 5, 6, /* packet 1 partial body */
+ };
+
+ byte buf2_data[TWOIN2_4_BUF2_BYTECOUNT]
+ = { 7, /* packet 1 body balance */
+ -34,-83,-66,-17, 0, 0, 0, 3, /* packet 2 header */
+ 8, 9, 10 /* packet 2 body */
+ };
+
+ byte expected_result[][TWOIN2_4_RESULT_BYTECOUNT] =
+ {
+ {
+ 5, 6, 7, /* packet 1 body */
+ },
+ {
+ 8, 9, 10, /* packet 2 body */
+ },
+ };
+
+ etch_nativearray* expected_result_array = new_etch_nativearray_from
+ (&expected_result, CLASSID_ARRAY_BYTE,
+ sizeof(byte), /* size of an array item is 1 */
+ 2, /* count of dimensions [2][2] */
+ 3, /* count of items in low order dimension */
+ 2, /* count of items in secondary dimension */
+ 0 /* 3rd dimension (not applicable) */
+ );
+
+ my_impl_sessionpacket* mysessionpkt = g_my_sessionpacket->thisx;
+
+ /* buffer 1 */
+ disposable_packet = etch_malloc(sizeof(buf1_data), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, buf1_data, sizeof(buf1_data));
+
+ g_mybuf = new_flexbuffer_from(disposable_packet, sizeof(buf1_data), 10, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_mybuf);
+ CU_ASSERT_EQUAL(g_mybuf->datalen, sizeof(buf1_data));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, g_mybuf);
+ CU_ASSERT_EQUAL(result,0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(mysessionpkt->sender);
+
+ /* buffer 2 */
+ disposable_packet = etch_malloc(sizeof(buf2_data), ETCHTYPEB_BYTES);
+ memcpy(disposable_packet, buf2_data, sizeof(buf2_data));
+
+ mybuf2 = new_flexbuffer_from(disposable_packet, sizeof(buf2_data), 0, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mybuf2);
+ CU_ASSERT_EQUAL(mybuf2->datalen, sizeof(buf2_data));
+
+ result = g_my_packetizer->session_data(g_my_packetizer, g_who, mybuf2);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = check_packetizer_resultx(g_my_sessionpacket, expected_result_array);
+ CU_ASSERT_EQUAL(result, 0);
+
+ CU_ASSERT_EQUAL(mysessionpkt->what, WHAT_PACKET);
+ result = is_equal_who(mysessionpkt->sender, g_who);
+ CU_ASSERT_EQUAL(result, TRUE);
+ /* done - free memory */
+ etch_object_destroy(expected_result_array);
+ etch_object_destroy(g_mybuf); g_mybuf = NULL;
+ etch_object_destroy(mybuf2);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_control
+ * test the session control notification plumbing
+ */
+static void test_session_control(void)
+{
+ setup_this_test();
+
+ do
+ {
+ const int MY_CONTROL_CLASSID = 0x5200, MY_VALUE_CLASSID = 0x5201;
+ etch_object* mycontrolobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_CONTROL_CLASSID);
+ etch_object* myvalueobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_VALUE_CLASSID);
+
+ /* g_my_sessionpacket is the i_sessionpacket interface
+ * my_impl_sessionpacket is the implementing test class */
+ my_impl_sessionpacket* my_session = g_my_sessionpacket->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+ CU_ASSERT_EQUAL(my_session->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_session->control);
+ CU_ASSERT_PTR_NULL(my_session->value);
+
+ /* we relinquish memory for mycontrolobj and myvalueobj here.
+ * the session_control terminal destination must destroy them, which here
+ * is handled by our session object destructor when we teardown_this_test() */
+ g_my_packetizer->session_control (g_my_packetizer, (void*)mycontrolobj, myvalueobj);
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_CONTROL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->control);
+ CU_ASSERT_EQUAL(((etch_object*)my_session->control)->class_id, MY_CONTROL_CLASSID);
+ CU_ASSERT_EQUAL(((etch_object*)my_session->value)->class_id, MY_VALUE_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_notify
+ * test the session notify notification plumbing
+ */
+static void test_session_notify(void)
+{
+ setup_this_test();
+
+ do
+ {
+ const int MY_EVENT_CLASSID = 0x5202;
+ etch_object* myeventobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_EVENT_CLASSID);
+
+ /* g_my_sessionpacket is the i_sessionpacket interface
+ * my_impl_sessionpacket is the implementing test class */
+ my_impl_sessionpacket* my_session = g_my_sessionpacket->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+ CU_ASSERT_EQUAL(my_session->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_session->eventx);
+
+ /* we relinquish memory for myeventobj here.
+ * the session_control terminal destination must destroy it, which here
+ * is handled by our session object destructor when we teardown_this_test() */
+ g_my_packetizer->session_notify(g_my_packetizer, (void*)myeventobj);
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_NOTIFY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->eventx);
+ CU_ASSERT_EQUAL(((etch_object*)my_session->eventx)->class_id, MY_EVENT_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_session_query
+ * test the session query notification plumbing
+ */
+static void test_session_query(void)
+{
+ setup_this_test();
+
+ do
+ {
+ const int MY_QUERY_CLASSID = 0x5203, MY_RESULT_CLASSID = 0x5204;
+ etch_object* myqueryobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_QUERY_CLASSID);
+ etch_object* myresultobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_RESULT_CLASSID);
+ etch_object* queryresult = NULL;
+
+ /* g_my_sessionpacket is the i_sessionpacket interface
+ * my_impl_sessionpacket is the implementing test class */
+ my_impl_sessionpacket* my_session = g_my_sessionpacket->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session);
+ CU_ASSERT_EQUAL(my_session->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_session->query);
+
+ /* we relinquish myresultobj here. see comments following */
+ my_session->query_result = myresultobj;
+
+ /* we relinquish memory for myqueryobj here and assume queryresult.
+ * the session_control terminal destination must destroy it, which here
+ * is handled by our session object destructor when we teardown_this_test() */
+ queryresult = g_my_packetizer->session_query(g_my_packetizer, (etch_query*)myqueryobj);
+
+ CU_ASSERT_EQUAL(my_session->what, SESSION_QUERY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_session->query);
+ CU_ASSERT_EQUAL(((etch_object*)my_session->query)->class_id, MY_QUERY_CLASSID);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(queryresult);
+ CU_ASSERT_EQUAL(((etch_object*)queryresult)->class_id, MY_RESULT_CLASSID);
+ etch_object_destroy(queryresult);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_control
+ * test the transport control notification plumbing
+ */
+static void test_transport_control(void)
+{
+ setup_this_test();
+
+ do
+ {
+ const int MY_CONTROL_CLASSID = 0x5200, MY_VALUE_CLASSID = 0x5201;
+ etch_object* mycontrolobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_CONTROL_CLASSID);
+ etch_object* myvalueobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_VALUE_CLASSID);
+
+ /* g_my_transportdata is the i_transportdata interface
+ * my_impl_transportdata is the implementing test class */
+ my_impl_transportdata* my_transport = g_my_transportdata->thisx;
+ CU_ASSERT_FATAL(is_my_impl_transportdata(my_transport));
+ CU_ASSERT_EQUAL(my_transport->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport->control);
+ CU_ASSERT_PTR_NULL(my_transport->value);
+
+ /* we relinquish memory for mycontrolobj and myvalueobj here.
+ * the transport_control terminal destination must destroy them, which here
+ * is handled by our transport object destructor when we teardown_this_test() */
+ g_my_packetizer->transport_control(g_my_packetizer, (etch_event*)mycontrolobj, myvalueobj);
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_CONTROL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->control);
+ CU_ASSERT_EQUAL(((etch_object*)my_transport->control)->class_id, MY_CONTROL_CLASSID);
+ CU_ASSERT_EQUAL(((etch_object*)my_transport->value)->class_id, MY_VALUE_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_notify
+ * test the transport notify notification plumbing
+ */
+static void test_transport_notify(void)
+{
+ setup_this_test();
+
+ do
+ {
+ const int MY_EVENT_CLASSID = 0x5202;
+ etch_object* myeventobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_EVENT_CLASSID);
+
+ /* g_my_transportdata is the i_transportdata interface
+ * my_impl_transportdata is the implementing test class */
+ my_impl_transportdata* my_transport = g_my_transportdata->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+ CU_ASSERT_EQUAL(my_transport->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport->control);
+ CU_ASSERT_PTR_NULL(my_transport->value);
+
+ /* we relinquish memory for myeventobj here.
+ * the transport_control terminal destination must destroy it, which here
+ * is handled by our transport object destructor when we teardown_this_test() */
+ g_my_packetizer->transport_notify(g_my_packetizer, (etch_event*)myeventobj);
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_NOTIFY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->eventx);
+ CU_ASSERT_EQUAL(((etch_object*)my_transport->eventx)->class_id, MY_EVENT_CLASSID);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_transport_query
+ * test the transport query notification plumbing
+ */
+static void test_transport_query(void)
+{
+ setup_this_test();
+
+ do
+ {
+ const int MY_QUERY_CLASSID = 0x5203, MY_RESULT_CLASSID = 0x5204;
+ etch_object* myqueryobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_QUERY_CLASSID);
+ etch_object* myresultobj = new_object(sizeof(etch_object),ETCHTYPEB_ETCHOBJECT, MY_RESULT_CLASSID);
+ etch_object* queryresult = NULL;
+
+ /* g_my_transportdata is the i_transportdata interface
+ * my_impl_transportdata is the implementing test class */
+ my_impl_transportdata* my_transport = g_my_transportdata->thisx;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport);
+ CU_ASSERT_EQUAL(my_transport->what, WHAT_NONE);
+ CU_ASSERT_PTR_NULL(my_transport->query);
+
+ /* we relinquish myresultobj here. see comments following */
+ my_transport->query_result = myresultobj;
+
+ /* we relinquish memory for myqueryobj here and assume queryresult.
+ * the transport_control terminal destination must destroy it, which here
+ * is handled by our transport object destructor when we teardown_this_test() */
+ queryresult = g_my_packetizer->transport_query(g_my_packetizer, (etch_query*)myqueryobj);
+
+ CU_ASSERT_EQUAL(my_transport->what, TRANSPORT_QUERY);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(my_transport->query);
+ CU_ASSERT_EQUAL(((etch_object*)my_transport->query)->class_id, MY_QUERY_CLASSID);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(queryresult);
+ CU_ASSERT_EQUAL(((etch_object*)queryresult)->class_id, MY_RESULT_CLASSID);
+ etch_object_destroy(queryresult);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_packetizer_suite()
+{
+ CU_pSuite ps = CU_add_suite("suite_packetizer", init_suite, clean_suite);
+
+ CU_add_test(ps, "test sessionpacket ctor/dtor", test_sessionpacket_constructor);
+ CU_add_test(ps, "test transportdata ctor/dtor", test_transportdata_constructor);
+ CU_add_test(ps, "test packetizer ctor/dtor", test_packetizer_constructor);
+ CU_add_test(ps, "test unit test setup/teardown", test_test_setup);
+ CU_add_test(ps, "test packet1 to source", test_to_source_packet1);
+ CU_add_test(ps, "test packet2 to source", test_to_source_packet2);
+ CU_add_test(ps, "test packet3 to source", test_to_source_packet3);
+ CU_add_test(ps, "test bad packet 0", test_to_source_badpacket1);
+ CU_add_test(ps, "test bad packet 1", test_badpacket1);
+
+ CU_add_test(ps, "test 1 packet 1 buffer", test_single_single_data);
+ CU_add_test(ps, "test 2 in 1 buffer 1", test_two_in_one_buffer_data_1);
+ CU_add_test(ps, "test 2 in 1 buffer 2", test_two_in_one_buffer_data_2);
+ CU_add_test(ps, "test 2 in 1 buffer 3", test_two_in_one_buffer_data_3);
+ CU_add_test(ps, "test 2 in 1 buffer 4", test_two_in_one_buffer_data_4);
+ CU_add_test(ps, "test 2 in 1 buffer 5", test_two_in_one_buffer_data_5);
+ CU_add_test(ps, "test 2 in 2 buffers 1", test_two_in_two_buffers_data_1);
+ CU_add_test(ps, "test 2 in 2 buffers 2", test_two_in_two_buffers_data_2);
+ CU_add_test(ps, "test 2 in 2 buffers 3", test_two_in_two_buffers_data_3);
+ CU_add_test(ps, "test 2 in 2 buffers 4", test_two_in_two_buffers_data_4);
+
+ CU_add_test(ps, "test session control", test_session_control);
+ CU_add_test(ps, "test session notify", test_session_notify);
+ CU_add_test(ps, "test session query", test_session_query);
+ CU_add_test(ps, "test transport control", test_transport_control);
+ CU_add_test(ps, "test transport notify", test_transport_notify);
+ CU_add_test(ps, "test transport query", test_transport_query);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/transport/test_plainmailbox.c b/binding-c/runtime/c/src/test/transport/test_plainmailbox.c
new file mode 100644
index 0000000..47a20d4
--- /dev/null
+++ b/binding-c/runtime/c/src/test/transport/test_plainmailbox.c
@@ -0,0 +1,1338 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_plainmailbox.c
+ */
+#include "etch_runtime.h"
+#include "etch_plain_mailbox.h"
+#include "etch_plain_mailbox_manager.h"
+#include "etch_default_value_factory.h"
+#include "etch_map.h"
+#include "etch_log.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_simpletimer.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - -
+ */
+
+static etch_plainmailboxmgr* g_manager;
+static etch_arraylist* g_list;
+static etch_who* g_who1;
+static etch_who* g_who2;
+static etch_type* g_type1;
+static etch_type* g_type2;
+static etch_mutex* g_rwlock;
+static default_value_factory* g_vf;
+static vf_idname_map* g_typemap;
+static class_to_type_map* g_c2tmap;
+
+
+static int g_is_unregistered;
+static int g_mailbox_status;
+static int g_mailbox_isclosed;
+static int g_wakeupreason;
+static i_mailbox* g_mailbox;
+static void* g_wakeupdata;
+static etch_object* g_mailbox_state;
+static etch_object_destructor g_list_stockdtor;
+#define CLASSID_MYSTATE 0xf0
+#define MYSTATE_VALUE 12345
+
+
+/**
+ * my_redelivery_list_destructor()
+ * custom dtor for message redelivery sink.
+ * logs and destroys redelivered messages.
+ */
+static int my_redelivery_list_destructor(void* data)
+{
+ etch_arraylist* list = (etch_arraylist*)data;
+ etch_iterator iterator;
+ set_iterator(&iterator, list, &list->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ etch_mailbox_element* listitem = iterator.current_value;
+ #if IS_DEBUG_CONSOLE
+ ETCH_LOG("TEST", ETCH_LOG_INFO, "destroying message %d from redelivery sink\n", msgid);
+ #endif
+
+ etch_object_destroy(listitem); /* destroy message and element wrapper */
+
+ iterator.next(&iterator);
+ }
+
+ return g_list_stockdtor(list); /* invoke original list destructor */
+}
+
+/**
+ * mymboxmgr_unregister()
+ * override for mailbox manager unregister
+ */
+static int mymboxmgr_unregister (void* mgr, i_mailbox* mbox)
+{
+ g_is_unregistered = TRUE;
+ return 0;
+}
+
+
+/**
+ * mymboxmgr_redeliver()
+ * override for mailbox manager redeliver
+ */
+static int mymboxmgr_redeliver (void* mgr, etch_who* whofrom, etch_message* msg)
+{
+ etch_arraylist_add(g_list, new_mailbox_element(msg, whofrom));
+ return 0;
+}
+
+/**
+ * mymboxmgr_redeliver()
+ * override for mailbox notify
+ */
+static int my_mailbox_notify (void* data, i_mailbox* mb, void* stateData, const int is_closed)
+{
+ etch_object* state = (etch_object*)stateData;
+ g_mailbox = mb;
+ g_mailbox_state = state;
+ g_mailbox_status = TRUE;
+ g_mailbox_isclosed = is_closed;
+ return 0;
+}
+
+
+/**
+ * setup_this_test()
+ * set up an individual unit test
+ */
+static int setup_this_test()
+{
+ // TODO: pool
+ etch_mutex_create(&g_rwlock, ETCH_MUTEX_NESTED, NULL);
+
+ g_manager = new_plain_mailbox_manager (NULL, NULL, NULL, g_rwlock);
+ if (NULL == g_manager) return -1;
+ g_manager->unregister = g_manager->imanager->unregister = mymboxmgr_unregister;
+ g_manager->redeliver = g_manager->imanager->redeliver = mymboxmgr_redeliver;
+
+ /* message redelivery sink */
+ g_list = new_etch_arraylist(0, 0);
+ g_list->content_type = ETCHARRAYLIST_CONTENT_OBJECT;
+ g_list->is_readonly = TRUE;
+ g_list_stockdtor = ((etch_object*)g_list)->destroy; /* custom destructor will destroy list content */
+ ((etch_object*)g_list)->destroy = my_redelivery_list_destructor;
+
+ g_who1 = new_who(new_int32(1));
+ g_who2 = new_who(new_int32(2));
+ g_type1 = new_type(L"type1");
+ g_type2 = new_type(L"type2");
+
+ g_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(g_typemap);
+ g_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(g_c2tmap);
+ defvf_initialize_static(g_typemap, g_c2tmap);
+ g_vf = new_default_value_factory(g_typemap, g_c2tmap);
+ CU_ASSERT_PTR_NOT_NULL(g_vf);
+
+ etchtype_put_validator(g_type1, clone_field(builtins._mf__message_id), (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(g_type2, clone_field(builtins._mf__message_id), (etch_object*) etchvtor_int64_get(0));
+
+ return g_manager? 0: -1;
+}
+
+
+/**
+ * teardown_this_test()
+ * tear down an individual unit test
+ */
+static void teardown_this_test()
+{
+ etch_object_destroy(g_manager);
+ g_manager = NULL;
+
+ etch_mutex_destroy(g_rwlock);
+ g_rwlock = NULL;
+
+ etch_object_destroy(g_list);
+ g_list = NULL;
+
+ etch_object_destroy(g_who1);
+ g_who1 = NULL;
+
+ etch_object_destroy(g_who2);
+ g_who2 = NULL;
+
+ etch_object_destroy(g_type1);
+ g_type1 = NULL;
+
+ etch_object_destroy(g_type2);
+ g_type2 = NULL;
+
+ etch_object_destroy(g_vf);
+ g_vf = NULL;
+
+ etch_object_destroy(g_c2tmap);
+ g_c2tmap = NULL;
+
+ etch_object_destroy(g_typemap);
+ g_typemap = NULL;
+
+ // TODO: cleanup statics
+ //etchvf_free_builtins()
+
+ g_is_unregistered = g_mailbox_status = g_mailbox_isclosed = g_wakeupreason = 0;
+ g_mailbox = NULL;
+ g_mailbox_state = NULL;
+ g_wakeupdata = NULL;
+ etchvf_free_builtins();
+}
+
+
+
+
+
+
+static void check_mailbox_status(const int expected_status, i_mailbox* expected_mbox,
+ etch_object* expected_state, const int expected_isclosed)
+{
+ CU_ASSERT_EQUAL(g_mailbox_status, expected_status);
+ CU_ASSERT_EQUAL(g_mailbox_state, expected_state);
+ CU_ASSERT_EQUAL(g_mailbox_isclosed, expected_isclosed);
+ CU_ASSERT_PTR_EQUAL(g_mailbox, expected_mbox);
+}
+
+
+static void check_mailbox(etch_plainmailbox* mbox, const int is_expected_empty,
+ const int is_expected_full, const int is_expected_closed,
+ const int is_expected_unregistered, const int expected_size)
+{
+ i_mailbox* ibox = mbox->imailbox;
+ int result = mbox->is_empty(ibox);
+ CU_ASSERT_EQUAL(result, is_expected_empty);
+ result = mbox->is_full(ibox);
+ CU_ASSERT_EQUAL(result, is_expected_full);
+ result = mbox->is_closed(ibox);
+ CU_ASSERT_EQUAL(result, is_expected_closed);
+ CU_ASSERT_EQUAL(g_is_unregistered, is_expected_unregistered);
+ CU_ASSERT_EQUAL(g_list->count, expected_size);
+}
+
+
+static void check_deliver(etch_plainmailbox* mbox, const int expected_ishandled,
+ etch_who* who, etch_message* msg)
+{
+ int actual_result;
+
+ const int delivery_result = mbox->message(mbox->imailbox, who, msg);
+
+ if (delivery_result == 0 && expected_ishandled)
+ actual_result = 0;
+ else
+ if (delivery_result != 0 && !expected_ishandled)
+ actual_result = 0;
+ else
+ actual_result = -1;
+
+ CU_ASSERT_EQUAL(actual_result, 0);
+
+ if (0 != delivery_result) /* caller retains message memory on failure */
+ if (delivery_result != ETCH_MAILBOX_DUPLICATE)
+ etch_object_destroy(msg);
+}
+
+
+static void check_redelivered(etch_plainmailbox* mbox, const int index, etch_message* msg)
+{
+ etch_mailbox_element* entry = etch_arraylist_get(g_list, index);
+ CU_ASSERT_PTR_NOT_NULL(entry);
+ if (NULL == entry) return;
+ CU_ASSERT_PTR_EQUAL(entry->msg, msg);
+}
+
+
+static void check_close_delivery(etch_plainmailbox* mbox, const int expected_is_closed)
+{
+ const int did_close = 0 == mbox->close_delivery(mbox->imailbox);
+ CU_ASSERT_EQUAL(did_close, expected_is_closed);
+}
+
+
+static void check_close_read(etch_plainmailbox* mbox, const int expected_is_closed)
+{
+ const int did_close = 0 == mbox->close_read(mbox->imailbox);
+ CU_ASSERT_EQUAL(did_close, expected_is_closed);
+}
+
+
+static void check_element(etch_mailbox_element* elt, etch_who* who, etch_message* msg)
+{
+ CU_ASSERT_PTR_NOT_NULL(elt);
+ if (NULL == elt) return;
+ CU_ASSERT_PTR_EQUAL(who, elt->whofrom);
+ CU_ASSERT_PTR_EQUAL(msg, elt->msg);
+}
+
+
+static void check_read(etch_plainmailbox* mbox, const int expected_to_be_present,
+ etch_who* who, etch_message* msg)
+{
+ etch_mailbox_element* thiselt = NULL;
+
+ mbox->read(mbox->imailbox, &thiselt);
+
+ if (expected_to_be_present)
+ check_element(thiselt, who, msg);
+ else { CU_ASSERT(is_etch_exception(thiselt)); }
+
+ /* message is "processed", end of the line for the message */
+ etch_object_destroy(thiselt);
+}
+
+
+static void check_read_withwait(etch_plainmailbox* mbox, const int maxdelay,
+ const int expected_to_be_present, etch_who* who, etch_message* msg)
+{
+ etch_mailbox_element* thiselt = NULL;
+
+ mbox->read_withwait(mbox->imailbox, maxdelay, &thiselt);
+
+ if (expected_to_be_present)
+ check_element(thiselt, who, msg);
+ else { CU_ASSERT(is_etch_exception(thiselt)); }
+
+ /* message is "processed", end of the line for the message */
+ etch_object_destroy(thiselt);
+}
+
+
+/**
+ * my_alarm_wakeup()
+ */
+static void my_alarm_wakeup (void* passthrudata, const int wakeupreason)
+{
+ etch_plainmailbox* thisx = (etch_plainmailbox*) passthrudata;
+ g_wakeupdata = thisx;
+ g_wakeupreason = wakeupreason;
+}
+
+
+/* - - - - - - - - - - - - - -
+ * unit tests
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_mailboxes_map()
+ * test hashtable configured as for mapping id object to mailbox object,
+ * insert, locate, remove, destroy, verify all memory accounted for.
+ */
+static void test_mailboxes_map(void)
+{
+ int result = 0;
+ const int64 val1 = 1234567890LL;
+ const int64 val2 = 9876543210LL;
+ etch_hashtable* map = new_pmboxmgr_mailboxmap();
+ etch_hashitem hi, *thisitem = &hi;
+ i_mailbox* mbox1 = new_default_mailbox_interface(NULL);
+ i_mailbox* mbox2 = new_default_mailbox_interface(NULL);
+ etch_int64* msgid1 = new_int64(val1);
+ etch_int64* msgid2 = new_int64(val2);
+ etch_int64* foundkey = NULL;
+ const unsigned expecthash1 = etchhash(&val1, 8, 0);
+ const unsigned expecthash2 = etchhash(&val2, 8, 0);
+ CU_ASSERT_EQUAL(((etch_object*)msgid1)->get_hashkey(msgid1), expecthash1);
+ CU_ASSERT_EQUAL(((etch_object*)msgid2)->get_hashkey(msgid2), expecthash2);
+
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->inserth(map->realtable, msgid1, mbox1, map, 0);
+ CU_ASSERT_EQUAL(result,0);
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->inserth(map->realtable, msgid2, mbox2, map, 0);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->findh(map->realtable, ((etch_object*)msgid1)->hashkey, map, (void*)&thisitem);
+ CU_ASSERT_EQUAL(result,0);
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->findh(map->realtable, ((etch_object*)msgid2)->hashkey, map, (void*)&thisitem);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = ((struct i_hashtable*)((etch_object*)map)->vtab)->removeh(map->realtable, ((etch_object*)msgid1)->hashkey, map, (void*)&thisitem);
+ CU_ASSERT_EQUAL_FATAL(result,0);
+ foundkey = (etch_int64*) thisitem->key;
+ CU_ASSERT_EQUAL_FATAL(is_etch_int64(foundkey), TRUE);
+ CU_ASSERT_EQUAL(foundkey->value, msgid1->value);
+ etch_free(foundkey);
+
+ etch_object_destroy(map);
+
+ /* map owns its keys and does not own its values */
+ etch_free(mbox1);
+ etch_free(mbox2);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_setup_teardown()
+ * test the test setup/teardown and verify all memory accounted for.
+ * passing this test ensures that any leaks that might show up in subsequeuent
+ * tests are specific to the test, not the common test data.
+ */
+static void test_setup_teardown(void)
+{
+ etch_message* test_message;
+
+ int result = setup_this_test();
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ /* test that messages added to the global list are destroyed at teardown */
+ test_message = new_message(g_type1, 0, (etch_value_factory*) g_vf);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(test_message);
+ etch_arraylist_add(g_list, new_mailbox_element(test_message, g_who1));
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_constructor()
+ * test the plain mailbox constructor
+ */
+static void test_constructor(void)
+{
+ setup_this_test();
+
+ do
+ { etch_plainmailbox* mbox = NULL;
+ const int64 THISMSGID = 9876543210LL;
+ const int QUEUEDELAY_1SEC = 1000, LIFETIME_TWO_MINUTES = 120000;
+ const int MAXMSGS_ONE = 1, QUEUEEWAITFOREVER = 0, QUEUENODELAY = -1;
+ printf("\n");
+
+ mbox = new_mailbox (g_manager->imanager, THISMSGID,
+ QUEUEDELAY_1SEC, MBOX_LIFETIME_UNTIL_CLOSE, MAXMSGS_ONE);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mbox);
+ CU_ASSERT_EQUAL(THISMSGID, mbox->get_message_id(mbox->imailbox));
+ CU_ASSERT_EQUAL(QUEUEDELAY_1SEC, mbox->max_message_delay);
+ CU_ASSERT_EQUAL(MBOX_LIFETIME_UNTIL_CLOSE, mbox->lifetime);
+ CU_ASSERT_EQUAL(MAXMSGS_ONE, mbox->max_messages);
+ etch_object_destroy(mbox);
+
+ mbox = new_mailbox (g_manager->imanager, THISMSGID,
+ QUEUENODELAY, MBOX_LIFETIME_UNTIL_CLOSE, MAXMSGS_ONE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mbox);
+ etch_object_destroy(mbox);
+
+ mbox = new_mailbox (g_manager->imanager, THISMSGID,
+ QUEUEDELAY_1SEC, MBOX_LIFETIME_UNTIL_CLOSE, MAXMSGS_ONE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mbox);
+ etch_object_destroy(mbox);
+
+ /* testing with a nonzero lifetime here, this is atypical */
+ mbox = new_mailbox (g_manager->imanager, THISMSGID,
+ QUEUEEWAITFOREVER, LIFETIME_TWO_MINUTES, MAXMSGS_ONE);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mbox);
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_deliver()
+ * test message deliver
+ */
+static void test_deliver(void)
+{
+ setup_this_test();
+
+ do
+ { etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ check_mailbox(mbox, TRUE, FALSE, FALSE, FALSE, 0);
+
+ check_close_delivery(mbox, TRUE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+
+ check_deliver(mbox, FALSE, g_who1, new_message(g_type1, 0, (etch_value_factory*) g_vf));
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_close_delivery1()
+ * open box and close it for delivery while empty
+ */
+static void test_closedelivery1(void)
+{
+ setup_this_test();
+
+ do
+ { etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ check_close_delivery(mbox, TRUE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_close_delivery2()
+ * open box and close it for delivery while non-empty
+ */
+static void test_closedelivery2(void)
+{
+ setup_this_test();
+
+ do
+ { etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ etch_message *mymessage1 = new_message(g_type1, 0, (etch_value_factory*) g_vf);
+ message_set_id(mymessage1, new_int64(1));
+
+ check_deliver(mbox, TRUE, g_who1, mymessage1);
+ check_mailbox(mbox, FALSE, FALSE, FALSE, FALSE, 0);
+
+ check_close_delivery(mbox, TRUE);
+ check_mailbox(mbox, FALSE, FALSE, TRUE, TRUE, 0);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_close_delivery3()
+ * open box and close it for delivery twice
+ */
+static void test_closedelivery3(void)
+{
+ setup_this_test();
+
+ do
+ { etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+
+ check_close_delivery(mbox, TRUE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+
+ check_close_delivery(mbox, FALSE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_notify1()
+ * test message notify
+ */
+static void test_notify1(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_object* mystateobj = NULL;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ i_mailbox* ibox = mbox->imailbox;
+ check_mailbox_status(FALSE, NULL, NULL, FALSE);
+
+ mystateobj = (etch_object*)new_int32(MYSTATE_VALUE);
+
+ result = mbox->register_notify(ibox, my_mailbox_notify, mystateobj, ETCH_INFWAIT);
+
+ CU_ASSERT_EQUAL(result, 0);
+ check_mailbox_status(FALSE, NULL, NULL, FALSE);
+
+ check_close_delivery(mbox, TRUE);
+ check_mailbox_status(TRUE, ibox, mystateobj, TRUE);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_timer()
+ * test current implementation of timer
+ */
+static void test_timer(void)
+{
+ setup_this_test();
+
+ /* this test breaks if breakpoints are set in the underlying wait objects
+ * and thread procedures. this will of course be due to wait expiration
+ * and subsequent object destruction, however we should have better state
+ * maintenance in the thread, mutex, wait, and timer objects in order to
+ * be able to detect and avoid such problems.
+ */
+ do
+ { const int ONE_SECOND = 1000, TWO_SECONDS = 2000, TEN_SECONDS = 10000;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ etch_timer* timer;
+ int result = 0;
+
+ timer = new_timer(ONE_SECOND, mbox, my_alarm_wakeup);
+ result = etch_timer_start(timer);
+ etch_sleep(TWO_SECONDS);
+ CU_ASSERT_PTR_EQUAL(g_wakeupdata, mbox);
+ CU_ASSERT_EQUAL(g_wakeupreason, ETCH_TIMER_REASON_TIMEOUT);
+ etch_object_destroy(timer);
+
+ g_wakeupdata = NULL;
+ timer = new_timer(TEN_SECONDS, mbox, my_alarm_wakeup);
+ result = etch_timer_start(timer);
+ etch_sleep(TWO_SECONDS);
+ etch_timer_stop(timer);
+ CU_ASSERT_PTR_EQUAL(g_wakeupdata, mbox);
+ CU_ASSERT_EQUAL(g_wakeupreason, ETCH_TIMER_REASON_INTERRUPTED);
+ etch_object_destroy(timer);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_notify2()
+ * test message notify with mailbox expiration
+ */
+static void test_notify2(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_object* mystateobj = NULL;
+ const int LIFETIME_ONE_SECOND = 1000, TWO_SECONDS = 2000;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ i_mailbox* ibox = mbox->imailbox;
+ check_mailbox_status(FALSE, NULL, NULL, FALSE);
+ mystateobj = (etch_object*)new_int32(MYSTATE_VALUE);
+
+ result = mbox->register_notify(ibox, my_mailbox_notify, mystateobj, LIFETIME_ONE_SECOND);
+
+ CU_ASSERT_EQUAL(result, 0);
+ check_mailbox_status(FALSE, NULL, NULL, FALSE);
+
+ etch_sleep(TWO_SECONDS);
+ check_mailbox_status(TRUE, ibox, mystateobj, TRUE);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_notify3()
+ * test message notify
+ */
+static void test_notify3(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_object* mystateobj = NULL;
+ const int LIFETIME_FOREVER = 0, TWO_SECONDS = 2000;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ check_mailbox_status(FALSE, NULL, NULL, FALSE);
+ mystateobj = (etch_object*)new_int32(MYSTATE_VALUE);
+
+ result = mbox->register_notify(mbox->imailbox, my_mailbox_notify, mystateobj, LIFETIME_FOREVER);
+
+ CU_ASSERT_EQUAL(result, 0);
+ check_mailbox_status(FALSE, NULL, NULL, FALSE);
+
+ etch_sleep(TWO_SECONDS);
+ check_mailbox_status(FALSE, NULL, NULL, FALSE);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_notify4()
+ * test message notify.
+ * test that notify callback receives expected results on a successful queue of
+ * a message to a mailbox.
+ */
+static void test_notify4(void)
+{
+ setup_this_test();
+
+ do
+ { int result = 0;
+ etch_object* mystateobj = NULL;
+ etch_message* mymessage = NULL;
+ const int LIFETIME_FOREVER = 0, MYMSGID = 1337;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ i_mailbox* ibox = mbox->imailbox;
+ check_mailbox_status(FALSE, NULL, NULL, FALSE);
+ mystateobj = (etch_object*)new_int32(MYSTATE_VALUE);
+
+ result = mbox->register_notify(ibox, my_mailbox_notify, mystateobj, LIFETIME_FOREVER);
+
+ CU_ASSERT_EQUAL(result, 0);
+ check_mailbox_status(FALSE, NULL, NULL, FALSE);
+
+ mymessage = new_message(g_type1, 0, (etch_value_factory*) g_vf);
+ message_set_id(mymessage, new_int64(MYMSGID));
+ printf("\n");
+
+ /* relinquish message on successful queue of message to mailbox, retain message
+ * on other than success. possible results: 0, -1, ETCH_MAILBOX_TIMEOUT.
+ */
+ result = mbox->message(ibox, g_who2, mymessage);
+
+ CU_ASSERT_EQUAL(result, 0);
+ if (0 != result)
+ etch_object_destroy(mymessage);
+
+ check_mailbox_status(TRUE, ibox, mystateobj, FALSE);
+
+ /* destroying mailbox causes close which causes our message to be redelivered.
+ * our redelivery override causes that message to arrive in our redelivery list.
+ * that list and its message content is destroyed at teardown_this_test().
+ */
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_read1()
+ * test read message from mailbox and close box
+ */
+static void test_read1(void)
+{
+ setup_this_test();
+
+ do
+ { int MYMSGID = 1337;
+ etch_message* mymessage = NULL;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ check_mailbox(mbox, TRUE, FALSE, FALSE, FALSE, 0);
+ mymessage = new_message(g_type1, 0, (etch_value_factory*) g_vf);
+ message_set_id(mymessage, new_int64(MYMSGID));
+
+ check_deliver(mbox, TRUE, g_who2, mymessage);
+ check_mailbox(mbox, FALSE, FALSE, FALSE, FALSE, 0);
+
+ check_read(mbox, TRUE, g_who2, mymessage);
+ check_mailbox(mbox, TRUE, FALSE, FALSE, FALSE, 0);
+
+ check_close_delivery(mbox, TRUE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_read2()
+ * test close delivery before read message from mailbox
+ */
+static void test_read2(void)
+{
+ setup_this_test();
+
+ do
+ { int MYMSGID = 1337;
+ etch_message* mymessage = NULL;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ check_mailbox(mbox, TRUE, FALSE, FALSE, FALSE, 0);
+ mymessage = new_message(g_type1, 0, (etch_value_factory*) g_vf);
+ message_set_id(mymessage, new_int64(MYMSGID));
+
+ check_deliver(mbox, TRUE, g_who2, mymessage);
+ check_mailbox(mbox, FALSE, FALSE, FALSE, FALSE, 0);
+
+ check_close_delivery(mbox, TRUE);
+ check_mailbox(mbox, FALSE, FALSE, TRUE, TRUE, 0);
+
+ /* these checks were different in the java test, assuming it was wrong.
+ * java had us expecting to read something, however the queue was closed above
+ */
+ check_read(mbox, FALSE, g_who2, mymessage);
+ check_mailbox(mbox, FALSE, FALSE, TRUE, TRUE, 0);
+
+ /* mymessage remains queued in the mailbox. destroying the mailbox destroys the message */
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_read4()
+ */
+static void test_read4(void)
+{
+ setup_this_test();
+
+ do
+ {
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+
+ check_close_delivery(mbox, TRUE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+
+ check_read(mbox, FALSE, NULL, NULL);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_read6()
+ */
+static void test_read6(void)
+{
+ setup_this_test();
+
+ do
+ { const int LIFETIME_100 = 100;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, LIFETIME_100, 2);
+
+ check_close_delivery(mbox, TRUE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+
+ etch_sleep(2 * LIFETIME_100);
+
+ check_read(mbox, FALSE, NULL, NULL);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_read7()
+ */
+static void test_read7(void)
+{
+ setup_this_test();
+
+ do
+ { etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+
+ check_read_withwait(mbox, ETCH_NOWAIT, FALSE, NULL, NULL);
+ check_mailbox(mbox, TRUE, FALSE, FALSE, FALSE, 0);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_read8()
+ */
+static void test_read8(void)
+{
+ setup_this_test();
+
+ do
+ { const int MAXDELAY_1MS = 1;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+
+ check_read_withwait(mbox, MAXDELAY_1MS, FALSE, NULL, NULL);
+ check_mailbox(mbox, TRUE, FALSE, FALSE, FALSE, 0);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_closeread1()
+ * open box and close it for read while empty
+ */
+static void test_closeread1(void)
+{
+ setup_this_test();
+
+ do
+ { etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ check_close_read(mbox, TRUE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_closeread2()
+ * open box and close it for delivery while non-empty
+ */
+static void test_closeread2(void)
+{
+ setup_this_test();
+
+ do
+ { etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ etch_message *mymessage1 = new_message(g_type1, 0, (etch_value_factory*) g_vf);
+ message_set_id(mymessage1, new_int64(1));
+ #if IS_DEBUG_CONSOLE
+ printf("\n");
+ #endif
+
+ check_deliver(mbox, TRUE, g_who1, mymessage1);
+ check_mailbox(mbox, FALSE, FALSE, FALSE, FALSE, 0);
+
+ check_close_read(mbox, TRUE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 1);
+ check_redelivered(mbox, 0, mymessage1);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_closeread3()
+ * open box and close it for read twice while empty
+ */
+static void test_closeread3(void)
+{
+ setup_this_test();
+
+ do
+ { etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ check_close_read(mbox, TRUE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+ // currently we don't get an already closed return when the underlying queue has not
+ // yet been closed. this is benign, but we should really ensure that the queue is closed
+ // before mailbox state is set to either closed for read or shutdown.
+ // check_close_read(mbox, FALSE); // test disabled due to situation commented above
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 0);
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_closeread4()
+ * open box and close it for delivery twice while non-empty
+ */
+static void test_closeread4(void)
+{
+ setup_this_test();
+
+ do
+ { etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, 2);
+ etch_message *mymessage1 = new_message(g_type1, 0, (etch_value_factory*) g_vf);
+ message_set_id(mymessage1, new_int64(1));
+ #if IS_DEBUG_CONSOLE
+ printf("\n");
+ #endif
+
+ check_deliver(mbox, TRUE, g_who1, mymessage1);
+ check_mailbox(mbox, FALSE, FALSE, FALSE, FALSE, 0);
+
+ check_close_read(mbox, TRUE);
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 1);
+ check_redelivered(mbox, 0, mymessage1);
+
+ // check_close_read(mbox, FALSE); // test disabled - see test_closeread3 for comment
+ check_mailbox(mbox, TRUE, FALSE, TRUE, TRUE, 1);
+ check_redelivered(mbox, 0, mymessage1);
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_full1()
+ */
+static void test_full1(void)
+{
+ setup_this_test();
+
+ do
+ { const int CAPACITY_2 = 2;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, CAPACITY_2);
+ etch_message *mymessage1 = new_message(g_type1, 0, (etch_value_factory*) g_vf);
+ etch_message *mymessage2 = new_message(g_type2, 0, (etch_value_factory*) g_vf);
+ message_set_id(mymessage1, new_int64(1));
+ message_set_id(mymessage2, new_int64(2));
+ #if IS_DEBUG_CONSOLE
+ printf("\n");
+ #endif
+
+ check_mailbox(mbox, TRUE, FALSE, FALSE, FALSE, 0); /* should be empty */
+
+ check_deliver(mbox, TRUE, g_who1, mymessage1);
+ check_mailbox(mbox, FALSE, FALSE, FALSE, FALSE, 0); /* should be non-empty */
+
+ check_deliver(mbox, TRUE, g_who2, mymessage2);
+ check_mailbox(mbox, FALSE, TRUE, FALSE, FALSE, 0); /* should be full */
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_full2()
+ */
+static void test_full2(void)
+{
+ setup_this_test();
+
+ do
+ { const int CAPACITY_2 = 2;
+ etch_plainmailbox* mbox = new_mailbox (g_manager->imanager, 1, -1, 0, CAPACITY_2);
+ etch_message *mymessage1 = new_message(g_type1, 0, (etch_value_factory*) g_vf);
+ etch_message *mymessage2 = new_message(g_type2, 0, (etch_value_factory*) g_vf);
+ message_set_id(mymessage1, new_int64(1));
+ message_set_id(mymessage2, new_int64(2));
+ #if IS_DEBUG_CONSOLE
+ printf("\n");
+ #endif
+
+ check_deliver(mbox, TRUE, g_who1, mymessage1); /* deliver message 1 */
+ check_mailbox(mbox, FALSE, FALSE, FALSE, FALSE, 0); /* should be non-empty */
+
+ check_deliver(mbox, TRUE, g_who2, mymessage2); /* deliver message 2 */
+ check_mailbox(mbox, FALSE, TRUE, FALSE, FALSE, 0); /* box should be full */
+
+ check_deliver(mbox, FALSE, g_who1, mymessage2); /* deliver should fail */
+ check_mailbox(mbox, FALSE, TRUE, FALSE, FALSE, 0); /* box should still be full */
+
+ etch_object_destroy(mbox);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_plainmailbox_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("plain mailbox test suite", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test mailbox map", test_mailboxes_map);
+ CU_add_test(pSuite, "test setup teardown", test_setup_teardown);
+ CU_add_test(pSuite, "test constructor", test_constructor);
+
+ CU_add_test(pSuite, "test delivery", test_deliver);
+
+ CU_add_test(pSuite, "test close delivery 1", test_closedelivery1);
+ CU_add_test(pSuite, "test close delivery 2", test_closedelivery2);
+ CU_add_test(pSuite, "test close delivery 3", test_closedelivery3);
+
+ CU_add_test(pSuite, "test timer", test_timer);
+
+ CU_add_test(pSuite, "test notify a", test_notify1);
+ CU_add_test(pSuite, "test notify b", test_notify2);
+ CU_add_test(pSuite, "test notify c", test_notify3);
+ CU_add_test(pSuite, "test notify d", test_notify4);
+
+ CU_add_test(pSuite, "test read a", test_read1);
+ CU_add_test(pSuite, "test read b", test_read2);
+ CU_add_test(pSuite, "test read c", test_read4);
+ CU_add_test(pSuite, "test read d", test_read6);
+ CU_add_test(pSuite, "test read e", test_read7);
+ CU_add_test(pSuite, "test read f", test_read8);
+
+ CU_add_test(pSuite, "test close read 1", test_closeread1);
+ CU_add_test(pSuite, "test close read 2", test_closeread2);
+ CU_add_test(pSuite, "test close read 3", test_closeread3);
+ CU_add_test(pSuite, "test close read 4", test_closeread4);
+
+ CU_add_test(pSuite, "test full 1", test_full1);
+ CU_add_test(pSuite, "test full 2", test_full2);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/transport/test_queue.c b/binding-c/runtime/c/src/test/transport/test_queue.c
new file mode 100644
index 0000000..3f998e3
--- /dev/null
+++ b/binding-c/runtime/c/src/test/transport/test_queue.c
@@ -0,0 +1,739 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_queue.c
+ */
+#include "etch_runtime.h"
+#include "etch_queue.h"
+#include "etch_thread.h"
+#include "etch_objecttypes.h"
+#include "etch_threadpool_apr.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - -
+ */
+
+#define NUMBER_CONSUMERS 2
+#define CONSUMER_ACTIVITY 4
+#define NUMBER_PRODUCERS 3
+#define PRODUCER_ACTIVITY 5
+#define ERROR_BAD_THREADDATA 1
+static int g_error, g_producercount, g_consumercount, g_unique_id, g_pro, g_con;
+
+
+static unsigned next_test_id()
+{
+ apr_atomic_inc32 ((volatile apr_uint32_t*) &g_unique_id);
+ return g_unique_id;
+}
+
+static unsigned next_producer()
+{
+ apr_atomic_inc32 ((volatile apr_uint32_t*) &g_pro);
+ return g_pro;
+}
+
+static unsigned next_consumer()
+{
+ apr_atomic_inc32 ((volatile apr_uint32_t*) &g_con);
+ return g_con;
+}
+
+
+/**
+ * push_some_objects()
+ * push specified number of new objects onto the queue
+ */
+static int push_some_objects(etch_queue* q, const int howmany)
+{
+ int count = 0, result = 0;
+
+ while(count < howmany && result == 0)
+ result = etchqueue_put_withwait(q, ETCH_NOWAIT, new_int32(count++));
+
+ return result;
+}
+
+
+/**
+ * pop_some_objects()
+ * pop specified number of objects off the queue and destroy each object retrieved
+ */
+static int pop_some_objects(etch_queue* q, const int howmany)
+{
+ int count = 0, result = 0;
+ etch_object* thisobj = NULL;
+
+ while(count++ < howmany && result == 0)
+ {
+ result = etchqueue_get_withwait(q, ETCH_NOWAIT, (void**)&thisobj);
+ etch_object_destroy(thisobj);
+ thisobj = NULL;
+ }
+
+ return result;
+}
+
+
+#if 0
+/**
+ * pop_all_objects()
+ * pop all objects off the queue and destroy each object retrieved
+ */
+static int pop_all_objects(etch_queue* q)
+{
+ int popcount = 0, result = 0;
+ const int queuecount = etchqueue_size(q);
+ etch_object* thisobj = NULL;
+
+ while(popcount++ < queuecount && result == 0)
+ {
+ result = etchqueue_get_withwait(q, ETCH_NOWAIT, (void**)&thisobj);
+ if (thisobj)
+ thisobj->destroy(thisobj);
+ thisobj = NULL;
+ }
+
+ return result;
+}
+#endif
+
+/**
+ * mytest_threaddata()
+ * thread data for our test threads. we could pass the queue directly, but then
+ * we'd have to configure the threadpool to not destroy a thread's data.
+ */
+typedef struct mytest_threaddata { etch_queue* queue; } mytest_threaddata;
+
+#if 0
+/**
+ * new_testdata()
+ * constructor for test thread data
+ */
+static mytest_threaddata* new_testdata(etch_queue* q)
+{
+ mytest_threaddata* td = etch_malloc(sizeof(mytest_threaddata), 0);
+ td->queue = q;
+ return td;
+}
+#endif
+
+/**
+ * consumer_threadproc()
+ * consumer thread procedure. derived from APR queue test.
+ */
+static void consumer_threadproc(void *data)
+{
+ etch_thread_params* params = (etch_thread_params*) data;
+ etch_queue* queue = (etch_queue*) params->data;
+
+ int result = 0;
+ etch_int32* content = NULL;
+ int64 sleeprate = 1000000/CONSUMER_ACTIVITY;
+ next_consumer();
+
+ if (!is_etch_queue(queue)) /* can't test assert outside main thread */
+ {
+ printf("consumer_threadproc data is not etch_queue\n");
+ g_error = ERROR_BAD_THREADDATA;
+ return;
+ }
+
+ #if IS_DEBUG_CONSOLE
+ printf("start consumer thread %d\n", thisthread); fflush(stdout);
+ #endif
+
+ etch_sleep((rand() % 4) * 1000); /* sleep random seconds */
+
+ while(-1 != result) /* until shutdown ... */
+ {
+ g_consumercount++;
+
+ switch(result = etchqueue_get(queue, (void**)&content))
+ {
+ case -1:
+ break; /* shutdown or error */
+
+ case ETCH_QUEUE_OPERATION_CANCELED:
+ continue; /* signaled */
+
+ case ETCH_QUEUE_OPERATION_TIMEOUT:
+ default: /* timed out (try again) or success */
+
+ if (content)
+ {
+ #if IS_DEBUG_CONSOLE
+ printf("consumer thread %d popped content %d\n", thisthread, content->value); fflush(stdout);
+ #endif
+ etch_object_destroy(content);
+ content = NULL;
+ }
+ apr_sleep(sleeprate); /* sleep this long to achieve our rate */
+ }
+ }
+
+ #if IS_DEBUG_CONSOLE
+ printf("consumer thread %d exit\n", thisthread); fflush(stdout);
+ #endif
+}
+
+
+/**
+ * producer_threadproc()
+ * producer thread procedure. derived from APR queue test.
+ */
+static void producer_threadproc(void *data)
+{
+ etch_thread_params* params = (etch_thread_params*) data;
+ etch_queue* queue = (etch_queue*) params->data;
+
+ int result = 0, is_timeout = 0;
+ etch_int32* newcontent = NULL;
+ const int thisthread = next_producer();
+ int64 sleeprate = 1000000/PRODUCER_ACTIVITY;
+
+ if (!is_etch_queue(queue)) /* can't test assert outside main thread */
+ { printf("producer_threadproc data is not etch_queue\n");
+ g_error = ERROR_BAD_THREADDATA;
+ return;
+ }
+
+ #if IS_DEBUG_CONSOLE
+ printf("start producer thread %d\n", thisthread); fflush(stdout);
+ #endif
+
+ etch_sleep((rand() % 4) * 1000); /* sleep random seconds */
+
+ while(-1 != result) /* until shutdown ... */
+ {
+ g_producercount++;
+
+ if (is_timeout)
+ {
+ #ifdef IS_DEBUG_CONSOLE
+ printf("producer thread %d re-pushing content %d\n", thisthread, newcontent->value); fflush(stdout);
+ #endif
+ is_timeout = FALSE;
+ }
+ else
+ { newcontent = new_int32(next_test_id());
+ #if IS_DEBUG_CONSOLE
+ printf("producer thread %d pushing content %d\n", thisthread, newcontent->value); fflush(stdout);
+ #endif
+ }
+
+ switch(result = etchqueue_put(queue, newcontent))
+ {
+ case -1: /* queue closed or error */
+ etch_object_destroy(newcontent);
+ newcontent = NULL;
+ break;
+
+ case ETCH_QUEUE_OPERATION_CANCELED:
+ etch_object_destroy(newcontent);
+ newcontent = NULL;
+ continue; /* signaled */
+
+ case ETCH_QUEUE_OPERATION_TIMEOUT:
+ is_timeout = TRUE; /* fall thru ... */
+
+ default: /* timeout or success */
+ apr_sleep(sleeprate);
+ }
+ }
+
+ #if IS_DEBUG_CONSOLE
+ printf("producer thread %d exit\n", thisthread); fflush(stdout);
+ #endif
+}
+
+
+/* - - - - - - - - - - - - - -
+ * unit tests
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_constructor
+ */
+static void test_constructor(void)
+{
+ etch_queue* q = new_queue(ETCH_DEFSIZE);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(q);
+
+ etch_object_destroy(q);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_putget()
+ */
+static void test_putget(void)
+{
+ int result = 0, qdepth = 0;
+ etch_int32* thiscontent = NULL;
+ etch_int32* thiscontent1 = new_int32(1);
+ etch_int32* thiscontent2 = new_int32(2);
+ etch_queue* q = new_queue(ETCH_DEFSIZE);
+
+ result = etchqueue_put(q, thiscontent1);
+ CU_ASSERT_EQUAL(result,0);
+
+ qdepth = etchqueue_size(q);
+ CU_ASSERT_EQUAL(qdepth,1);
+
+ result = etchqueue_put(q, thiscontent2);
+ CU_ASSERT_EQUAL(result,0);
+
+ qdepth = etchqueue_size(q);
+ CU_ASSERT_EQUAL(qdepth,2);
+
+ result = etchqueue_get(q, (void**)&thiscontent);
+ CU_ASSERT_EQUAL(result,0);
+ result = is_etch_int32(thiscontent);
+ CU_ASSERT_EQUAL(result, TRUE);
+ CU_ASSERT_EQUAL(thiscontent->value, thiscontent1->value);
+
+ qdepth = etchqueue_size(q);
+ CU_ASSERT_EQUAL(qdepth,1);
+
+ result = etchqueue_get(q, (void**)&thiscontent);
+ CU_ASSERT_EQUAL(result,0);
+ result = is_etch_int32(thiscontent);
+ CU_ASSERT_EQUAL(result, TRUE);
+ CU_ASSERT_EQUAL(thiscontent->value, thiscontent2->value);
+
+ qdepth = etchqueue_size(q);
+ CU_ASSERT_EQUAL(qdepth,0);
+
+ etch_object_destroy(q);
+ etch_object_destroy(thiscontent1);
+ etch_object_destroy(thiscontent2);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_bulkputget()
+ */
+static void test_bulkputget(void)
+{
+ int result = 0, qdepth = 0;
+ const int HOWMANY = 3;
+ etch_queue* q = new_queue(HOWMANY);
+
+ result = push_some_objects(q, HOWMANY);
+ CU_ASSERT_EQUAL(result,0);
+
+ qdepth = etchqueue_size(q);
+ CU_ASSERT_EQUAL(qdepth, HOWMANY);
+
+ result = pop_some_objects(q, HOWMANY);
+ CU_ASSERT_EQUAL(result,0);
+
+ qdepth = etchqueue_size(q);
+ CU_ASSERT_EQUAL(qdepth, 0);
+
+ etch_object_destroy(q);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_destroy_nonempty_queue()
+ * destroy a nonempty queue and ensure content memory is freed as expected
+ */
+static void test_destroy_nonempty_queue(void)
+{
+ int result = 0;
+ const int HOWMANY = 3;
+ etch_queue* q = new_queue(HOWMANY);
+
+ result = push_some_objects(q, HOWMANY);
+
+ etch_object_destroy(q);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_queue_overflow()
+ */
+static void test_queue_overflow(void)
+{
+ int result = 0, qdepth = 0;
+ const int MAXDEPTH = 2;
+ etch_int32* newcontent = NULL;
+ etch_queue* q = new_queue(MAXDEPTH);
+
+ result = etchqueue_put_withwait(q, ETCH_NOWAIT, newcontent = new_int32(0));
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = etchqueue_put_withwait(q, ETCH_NOWAIT, newcontent = new_int32(0));
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = etchqueue_try_put(q, newcontent = new_int32(0));
+ CU_ASSERT_EQUAL(result, -1);
+
+ result = etchqueue_put_withwait(q, ETCH_NOWAIT, newcontent);
+ CU_ASSERT_NOT_EQUAL_FATAL(result, 0);
+ etch_object_destroy(newcontent);
+
+ qdepth = etchqueue_size(q);
+ CU_ASSERT_EQUAL(qdepth, MAXDEPTH);
+
+ etch_object_destroy(q);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_queue_underflow()
+ */
+static void test_queue_underflow(void)
+{
+ int result = 0, qdepth = 0;
+ const int MAXDEPTH = 2;
+ etch_int32* content = NULL;
+ etch_queue* q = new_queue(MAXDEPTH);
+
+ result = etchqueue_put_withwait(q, ETCH_NOWAIT, content = new_int32(0));
+ CU_ASSERT_EQUAL(result, 0);
+
+ result = etchqueue_get_withwait(q, ETCH_NOWAIT, (void**)&content);
+ CU_ASSERT_EQUAL(result, 0);
+ etch_object_destroy(content);
+ content = NULL;
+
+ result = etchqueue_try_get(q, (void**)&content);
+ CU_ASSERT_EQUAL(result, -1);
+
+ result = etchqueue_get_withwait(q, ETCH_NOWAIT, (void**)&content);
+ CU_ASSERT_NOT_EQUAL_FATAL(result, 0);
+
+ qdepth = etchqueue_size(q);
+ CU_ASSERT_EQUAL(qdepth, 0);
+
+ etch_object_destroy(q);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_iterator()
+ */
+static void test_iterator(void)
+{
+ etch_iterator iterator;
+ int iterated = 0;
+ const int HOWMANY = 3;
+
+ etch_queue* q = new_queue(ETCH_DEFSIZE);
+
+ push_some_objects(q, HOWMANY);
+
+ set_iterator(&iterator, q, &q->iterable);
+
+ while(iterator.has_next(&iterator))
+ {
+ etch_int32* content = (etch_int32*) iterator.current_value;
+ CU_ASSERT_EQUAL_FATAL(is_etch_int32(content), TRUE);
+ CU_ASSERT_EQUAL(content->value, iterated++);
+
+ iterator.next(&iterator);
+ }
+
+ CU_ASSERT_EQUAL(iterated, HOWMANY);
+
+ etch_object_destroy(q);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_blocking()
+ * derived from apr test_queue.
+ * launch some number of threads which blocking write etch objects to a queue,
+ * and some number of threads which blocking read from the same queue. let the
+ * threads do their thing for a while, and close the queue. verify that all
+ * memory is accounted for.
+ */
+static void test_blocking(void)
+{
+ #define NUMTHREADS NUMBER_CONSUMERS + NUMBER_PRODUCERS
+ int result = 0, i = 0;
+ etch_queue* myqueue = NULL;
+
+ etch_threadpool* threadpool = new_threadpool(ETCH_THREADPOOLTYPE_FREE, ETCH_DEFSIZE);
+ threadpool->is_free_data = FALSE; /* thread data will be our queue */
+
+ myqueue = new_queue(ETCH_DEFSIZE); /* create the single queue */
+ srand((unsigned int)apr_time_now());
+ g_error = g_producercount = g_consumercount = g_unique_id = g_pro = g_con = 0;
+
+ for(i=0; i < NUMBER_CONSUMERS; i++) /* launch all consumer threads */
+ { void* newthread = threadpool->run(threadpool, consumer_threadproc, myqueue);
+ CU_ASSERT_PTR_NOT_NULL(newthread);
+ }
+
+ for(i=0; i < NUMBER_PRODUCERS; i++) /* launch all producer threads */
+ { void* newthread = threadpool->run(threadpool, producer_threadproc, myqueue);
+ CU_ASSERT_PTR_NOT_NULL(newthread);
+ }
+
+ etch_sleep(3000); /* pause long enough to let the threads do some work */
+
+ result = etchqueue_close(myqueue, TRUE); /* mark queue closed and notify all waiters */
+
+ /* pause to observe waiter threads unblocking
+ etch_sleep(3000);
+ */
+
+ #ifdef TEST_BLOCKING_PRECLEAR
+ while(1)
+ { /* when the queue is closed it clears the queue similarly to this.
+ * this code is here to verify clearing the queue prior to closing it.
+ */
+ etch_int32* queuecontent = NULL;
+ result = etchqueue_get_withwait(myqueue, ETCHQUEUE_CLEARING_CLOSED_QUEUE, &queuecontent);
+ CU_ASSERT_EQUAL(result, -1);
+ if (-1 == result) break;
+ etch_object_destroy(queuecontent);
+ }
+ #endif
+
+ /* note that we should always destroy (or otherwise join) the queue
+ * accessor threads, before destroying the queue, as we do here */
+ etch_object_destroy(threadpool);
+
+ etch_object_destroy(myqueue);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/* - - - - - - - - - - - - - - - - - -
+ * APR queue test with APR threadpool
+ * - - - - - - - - - - - - - - - - - -
+ */
+
+static etch_apr_queue_t* g_queue; /* global queue for APR queue test */
+
+
+/**
+ * aprqtest_consumer_thread()
+ */
+static void *APR_THREAD_FUNC aprqtest_consumer_thread(apr_thread_t *thd, void *data)
+{
+ long sleeprate;
+ apr_status_t rv;
+ void *v;
+
+ sleeprate = 1000000/CONSUMER_ACTIVITY;
+ apr_sleep((rand() % 4) * 1000000); /* sleep random seconds */
+
+ while (1)
+ {
+ rv = etch_apr_queue_pop(g_queue, 0, &v);
+
+ if (rv == APR_EINTR)
+ continue;
+ if (rv == APR_EOF)
+ break;
+ apr_sleep(sleeprate); /* sleep this long to acheive our rate */
+ }
+
+ return NULL;
+}
+
+
+/**
+ * aprqtest_producer_thread()
+ */
+static void * APR_THREAD_FUNC aprqtest_producer_thread(apr_thread_t *thd, void *data)
+{
+ long sleeprate;
+ apr_status_t rv;
+
+ sleeprate = 1000000/PRODUCER_ACTIVITY;
+ apr_sleep((rand() % 4) * 1000000); /* sleep random seconds */
+
+ while (1)
+ {
+ rv = etch_apr_queue_push(g_queue, 0, NULL);
+
+ if (rv == APR_EINTR)
+ continue;
+ if (rv == APR_EOF)
+ break;
+ apr_sleep(sleeprate); /* sleep this long to acheive our rate */
+ }
+
+ return NULL;
+}
+
+
+/**
+ * test_queue_producer_consumer()
+ * this is the APR queue test modified to use etch_apr_queue calls.
+ * it uses the APR threadpool
+ */
+static void test_queue_producer_consumer(void)
+{
+ unsigned int i;
+ apr_status_t rv;
+ apr_thread_pool_t *threadpool;
+ const int QUEUE_SIZE = 32, ZEROPRIORITY = 0;
+ void* NULLPARAM = NULL, *NULLOWNER = NULL;
+ srand((unsigned int)apr_time_now());
+ g_error = g_producercount = g_consumercount = g_unique_id = g_pro = g_con = 0;
+
+ rv = etch_apr_queue_create(&g_queue, QUEUE_SIZE, g_etch_main_pool);
+ CU_ASSERT_EQUAL(rv,0);
+
+ rv = etch_apr_thread_pool_create(&threadpool, 0, NUMBER_CONSUMERS + NUMBER_PRODUCERS, g_etch_main_pool);
+ CU_ASSERT_EQUAL(rv,0);
+
+ for (i = 0; i < NUMBER_CONSUMERS; i++)
+ {
+ rv = etch_apr_thread_pool_push(threadpool, aprqtest_consumer_thread, NULLPARAM, ZEROPRIORITY, NULLOWNER);
+ CU_ASSERT_EQUAL(rv,0);
+ }
+
+ for (i = 0; i < NUMBER_PRODUCERS; i++)
+ {
+ rv = etch_apr_thread_pool_push(threadpool, aprqtest_producer_thread, NULLPARAM, ZEROPRIORITY, NULLOWNER);
+ CU_ASSERT_EQUAL(rv,0);
+ }
+
+ etch_sleep(5000); /* let threads work for a bit */
+
+ rv = etch_apr_queue_term(g_queue);
+ CU_ASSERT_EQUAL(rv,0);
+
+ /* pause to observe waiter threads unblocking
+ etch_sleep(3000);
+ */
+
+ rv = etch_apr_thread_pool_destroy(threadpool);
+ CU_ASSERT_EQUAL(rv,0);;
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_queue()
+{
+ CU_pSuite pSuite = CU_add_suite("queue test suite", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test constructor", test_constructor);
+ CU_add_test(pSuite, "test put and get", test_putget);
+ CU_add_test(pSuite, "test bulk put and get", test_bulkputget);
+ CU_add_test(pSuite, "test destroy nonempty", test_destroy_nonempty_queue);
+ CU_add_test(pSuite, "test overflow", test_queue_overflow);
+ CU_add_test(pSuite, "test underflow", test_queue_underflow);
+ CU_add_test(pSuite, "test iterator", test_iterator);
+ CU_add_test(pSuite, "test blocking", test_blocking);
+ CU_add_test(pSuite, "test producer-consumer (APR)", test_queue_producer_consumer);
+
+ return pSuite;
+}
diff --git a/binding-c/runtime/c/src/test/transport/test_tcpconn.c b/binding-c/runtime/c/src/test/transport/test_tcpconn.c
new file mode 100644
index 0000000..d65fb43
--- /dev/null
+++ b/binding-c/runtime/c/src/test/transport/test_tcpconn.c
@@ -0,0 +1,982 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_tcp_connection.c
+ */
+#include "etch_runtime.h"
+#include "etch_connection.h" /* must be included second */
+#include "etch_tcp_connection.h" /* must be included second */
+#include "etch_tcp_server.h"
+#include "etch_encoding.h"
+#include "etch_objecttypes.h"
+#include "etch_flexbuffer.h"
+#include "etch_general.h"
+#include "etch_thread.h"
+#include "etch_log.h"
+#include "etch_mem.h"
+#include <wchar.h>
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+static const char* LOG_CATEGORY = "test_etch_tcp_connection";
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+#define RCVBUFLEN 256
+#define TEST_PORT 7302
+#define TEST_IP L"127.0.0.1"
+static etch_url* g_test_url;
+static char* g_ip8bit;
+
+static char *g_senddata1="By the rude bridge that arched the flood, their flag from April's breeze unfurled";
+static char *g_senddata2="And therefore I have sailed the seas and come, to the holy city of Byzantium.";
+static int g_buflen1, g_buflen2;
+
+static etch_url* get_test_url() /* build a URL string for the test */
+{
+ wchar_t buf[256];
+ memset(buf, 0, sizeof(buf));
+ etch_snwprintf(buf, sizeof(buf), L"%ls:%d", TEST_IP, TEST_PORT);
+ return new_url(buf);
+}
+
+
+/**
+ * setup_a_test()
+ * set up a tcp server test by creating and returning an
+ * initialized tcp server object.
+ */
+static etch_tcp_server* setup_a_test()
+{
+ etch_tcp_server* tcp_server = 0;
+ etch_tcp_connection* cxl = 0;
+ etch_threadpool* pool = 0;
+ etch_connection* cx = 0;
+
+ /* create objects */
+ g_test_url = get_test_url();
+ CU_ASSERT_EQUAL_FATAL(wcscmp(g_test_url->host, TEST_IP), 0);
+ CU_ASSERT_EQUAL_FATAL(g_test_url->port, TEST_PORT);
+ // TODO: pool
+ etch_encoding_transcode_wchar(&g_ip8bit, ETCH_ENCODING_UTF8, TEST_IP, NULL);
+
+ pool = new_threadpool (ETCH_THREADPOOLTYPE_FREE, 0);
+
+ tcp_server = new_tcp_server(g_test_url, pool, pool, NULL, NULL);
+ CU_ASSERT_PTR_NOT_NULL(tcp_server);
+ tcp_server->is_threadpool_owned = TRUE; /* server now owns pool */
+
+ CU_ASSERT_EQUAL (tcp_server->state, ETCH_TCPSERVER_STATE_CLOSED);
+ CU_ASSERT_NOT_EQUAL (tcp_server->listener_id, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (tcp_server->cxlisten);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (tcp_server->on_event);
+
+ cxl = tcp_server->cxlisten; /* listen socket connection object */
+ cx = &cxl->cx;
+ CU_ASSERT_PTR_NULL_FATAL (cx->socket);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (cx->mutex);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (cx->listener);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (cx->on_event);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (cx->on_data);
+ CU_ASSERT_EQUAL_FATAL (cx->is_ownpool, TRUE);
+ CU_ASSERT_EQUAL_FATAL (cx->is_started, FALSE);
+
+ return tcp_server;
+}
+
+
+/**
+ * teardown_a_test()
+ * tear down a tcp server test by destroying the specified tcp server
+ * object plus the various objects created to set it up.
+ */
+static void teardown_a_test(etch_tcp_server* listener)
+{
+ etch_free(g_ip8bit);
+ etch_object_destroy(listener);
+ etch_object_destroy(g_test_url); g_test_url = NULL;
+}
+
+
+/**
+ * test_server_create_destroy()
+ * test that we can create a tcp server with all expected pieces present,
+ * and subsequently destroy it with all memory accounted for. also tests
+ * creation of a connection object (the server's listener connection).
+ * tests only construction and destruction, no other functionality.
+ * assuming we run this test early and it passes, we can safely omit
+ * a lot of asserts in subsequent tests.
+ */
+static void test_server_create_destroy(void)
+{
+ etch_threadpool* thread_manager = 0;
+ etch_tcp_server* tcp_server = 0;
+ etch_tcp_connection* cxl = 0;
+
+ etch_connection* cx = 0;
+ etch_url* url = 0;
+ #if IS_DEBUG_CONSOLE
+ wprintf(L"\n");
+ #endif
+
+ /* start - create objects */
+ url = get_test_url(); /* we own it */
+ CU_ASSERT_STRING_EQUAL_FATAL(url->host, TEST_IP);
+ CU_ASSERT_EQUAL_FATAL(url->port, TEST_PORT);
+
+ tcp_server = new_tcp_server(url, NULL, NULL, NULL, NULL);
+ CU_ASSERT_PTR_NOT_NULL(tcp_server);
+
+ CU_ASSERT_EQUAL (tcp_server->state, ETCH_TCPSERVER_STATE_CLOSED);
+ CU_ASSERT_EQUAL (tcp_server->listener_id, 2);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (tcp_server->cxlisten);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (tcp_server->on_event);
+
+ cxl = tcp_server->cxlisten; /* listen socket connection object */
+ cx = &cxl->cx;
+ CU_ASSERT_PTR_NULL_FATAL (cx->socket);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (cx->mutex);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (cx->listener);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (cx->on_event);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (cx->on_data);
+ CU_ASSERT_EQUAL_FATAL (cx->is_ownpool, TRUE);
+ CU_ASSERT_EQUAL_FATAL (cx->is_started, FALSE);
+
+ /* done - destroy objects */
+ etch_object_destroy(tcp_server);
+ etch_object_destroy(url);
+ etch_object_destroy(thread_manager);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_setup_a_test()
+ * test that we can do the same as test_server_create_destroy(), but using
+ * setup_a_test() and teardown_a_test to create and destroy objects.
+ */
+static void test_setup_a_test(void)
+{
+ etch_tcp_server* tcp_server = setup_a_test();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tcp_server);
+
+ teardown_a_test(tcp_server);
+}
+
+
+/**
+ * test_server_open_close()
+ * test that we can create, open, close, and destroy a tcp server
+ * with all memory accounted for. when server is open, its listen socket is
+ * listening on the designated port. when closed, it is not listening.
+ */
+static void test_server_open_close(void)
+{
+ int result;
+ etch_tcp_server* tcp_server = setup_a_test();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tcp_server);
+
+ result = etch_tcpsvr_open(tcp_server, 0);
+ CU_ASSERT_EQUAL(result, 0); /* state stopped means not closed but not started */
+ CU_ASSERT_EQUAL(tcp_server->state, ETCH_TCPSERVER_STATE_STOPPED);
+
+ if (result == 0)
+ { result = etch_tcpsvr_close(tcp_server);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(tcp_server->state, ETCH_TCPSERVER_STATE_CLOSED);
+ }
+
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_server_start_stop()
+ * test that we can create, open, start, stop, close, and destroy a tcp server
+ * with all memory accounted for. when server is started, it is accepting
+ * connections from clients.
+ */
+static void test_server_start_stop(void)
+{
+ int result;
+ etch_tcp_server* tcp_server = setup_a_test();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tcp_server);
+
+ result = etch_tcpsvr_open(tcp_server, 0);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpsvr_start(tcp_server);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL (tcp_server->threadpool);
+ CU_ASSERT_EQUAL_FATAL (tcp_server->is_started, TRUE);
+ CU_ASSERT_EQUAL_FATAL (tcp_server->state, ETCH_TCPSERVER_STATE_STARTED);
+
+ //getchar();
+ //etch_sleep(1000);
+
+ result = etch_tcpsvr_stop(tcp_server);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ CU_ASSERT_EQUAL_FATAL(tcp_server->is_started, FALSE);
+ CU_ASSERT_EQUAL_FATAL(tcp_server->state, ETCH_TCPSERVER_STATE_STOPPED);
+
+ result = etch_tcpsvr_close(tcp_server);
+ CU_ASSERT_EQUAL(result, 0);
+
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_server_control_start_stop()
+ */
+static void test_server_control_start_stop(void)
+{
+ int result = 0;
+ etch_event *startevent = NULL, *stopevent = NULL;
+
+ etch_tcp_server* tcp_server = setup_a_test();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tcp_server);
+
+ startevent = new_etch_event(CLASSID_CONTROL_START, 0); /* we relinquish this */
+ result = tcp_server->transport_control(tcp_server, startevent, NULL);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(tcp_server->is_started, TRUE);
+
+ stopevent = new_etch_event(CLASSID_CONTROL_STOP, 0); /* we relinquish this */
+ result = tcp_server->transport_control(tcp_server, stopevent, NULL);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(tcp_server->is_started, FALSE);
+
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_server_control_waitup_waitdown()
+ */
+static void test_server_control_waitup_waitdown(void)
+{
+ int result = 0;
+ etch_event *waitupevent = NULL, *waitdownevent = NULL;
+ etch_int32 *waituptime = NULL, *waitdowntime = NULL;
+
+ etch_tcp_server* tcp_server = setup_a_test();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tcp_server);
+
+ waitupevent = new_etch_event(CLASSID_CONTROL_START_WAITUP, 0); /* we relinquish this */
+ waituptime = new_int32(5000); /* we relinquish this */
+ result = tcp_server->transport_control(tcp_server, waitupevent, (etch_object*)waituptime);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(tcp_server->is_started, TRUE);
+
+ waitdownevent = new_etch_event(CLASSID_CONTROL_STOP_WAITDOWN, 0); /* we relinquish this */
+ waitdowntime = new_int32(5000); /* we relinquish this */
+ result = tcp_server->transport_control(tcp_server, waitdownevent, (etch_object*)waitdowntime);
+ CU_ASSERT_EQUAL(result, 0);
+ CU_ASSERT_EQUAL(tcp_server->is_started, FALSE);
+
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_connect_to_server()
+ * test that we can open and close a client connection to a tcp server.
+ */
+static void test_connect_to_server(void)
+{
+ int result;
+ etch_tcp_connection* cxclient = 0;
+ etch_tcp_server* tcp_server = setup_a_test();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tcp_server);
+
+ result = etch_tcpsvr_open(tcp_server, 0);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpsvr_start(tcp_server);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ etch_sleep(1000);
+
+ cxclient = new_tcp_connection(g_test_url, NULL, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient);
+
+ result = etch_tcpconx_open(cxclient, FALSE);
+
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ CU_ASSERT_STRING_EQUAL_FATAL(cxclient->cx.hostname, g_ip8bit);
+ CU_ASSERT_EQUAL_FATAL(cxclient->cx.port, TEST_PORT);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient->cx.on_event);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient->cx.on_data);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient->cx.aprpool);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient->cx.socket);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient->cx.mutex);
+
+ etch_sleep(500);
+
+ result = etch_tcpconx_close(cxclient, FALSE);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpsvr_stop(tcp_server);
+
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ CU_ASSERT_EQUAL_FATAL(tcp_server->is_started, FALSE);
+ CU_ASSERT_EQUAL_FATAL(tcp_server->state, ETCH_TCPSERVER_STATE_STOPPED);
+
+ result = etch_tcpsvr_close(tcp_server);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* free items created here */
+ etch_object_destroy(cxclient);
+
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+
+/**
+ * test_multiple_connect_to_server()
+ * test that we can open and close two concurrent connections to a tcp server.
+ */
+static void test_multiple_connect_to_server(void)
+{
+ int result;
+ etch_tcp_connection *cxclient_a = 0, *cxclient_b = 0;
+ etch_tcp_server* tcp_server = setup_a_test();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tcp_server);
+
+ result = etch_tcpsvr_open(tcp_server, 0);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpsvr_start(tcp_server);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ etch_sleep(500);
+
+ cxclient_a = new_tcp_connection(g_test_url, NULL, NULL);
+ cxclient_b = new_tcp_connection(g_test_url, NULL, NULL);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient_a);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient_b);
+
+ result = etch_tcpconx_open(cxclient_a, FALSE);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpconx_open(cxclient_b, FALSE);
+
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ CU_ASSERT_STRING_EQUAL_FATAL(cxclient_b->cx.hostname, g_ip8bit);
+ CU_ASSERT_EQUAL_FATAL(cxclient_b->cx.port, TEST_PORT);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient_b->cx.on_event);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient_b->cx.on_data);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient_b->cx.aprpool);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient_b->cx.socket);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient_b->cx.mutex);
+
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "client a ID is %d\n", cxclient_a->cx.conxid);
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "client b ID is %d\n", cxclient_b->cx.conxid);
+ etch_sleep(500);
+
+ result = etch_tcpconx_close(cxclient_a, FALSE);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpconx_close(cxclient_b, FALSE);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpsvr_stop(tcp_server);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ CU_ASSERT_EQUAL_FATAL(tcp_server->is_started, FALSE);
+ CU_ASSERT_EQUAL_FATAL(tcp_server->state, ETCH_TCPSERVER_STATE_STOPPED);
+
+ result = etch_tcpsvr_close(tcp_server);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* free items created here */
+ etch_object_destroy(cxclient_a);
+ etch_object_destroy(cxclient_b);
+
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_multiple_connect_to_server()
+ * test that we can open and close two concurrent connections to a tcp server.
+ */
+static void test_max_connections(void)
+{
+ int result;
+ int i = 0;
+ etch_tcp_connection **cxclient = malloc(40 * sizeof(etch_tcp_connection*));
+ etch_tcp_connection *cxclient_last = NULL;
+
+ etch_tcp_server* tcp_server = setup_a_test();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tcp_server);
+
+ result = etch_tcpsvr_open(tcp_server, 0);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpsvr_start(tcp_server);
+
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ etch_sleep(1000);
+
+ for(i = 0; i < 40; i++){
+ cxclient[i] = new_tcp_connection(g_test_url, NULL, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient[i]);
+ result = etch_tcpconx_open(cxclient[i],0);
+ etch_sleep(25);
+ CU_ASSERT(tcp_server->connections == i + 1);
+ }
+
+ CU_ASSERT(tcp_server->connections == 40);
+
+ cxclient_last = new_tcp_connection(g_test_url, NULL, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient_last);
+ result = etch_tcpconx_open(cxclient_last,0);
+
+ CU_ASSERT(tcp_server->connections == 40);
+ etch_sleep(200);
+
+ etch_tcpconx_close(cxclient_last,0);
+
+ for(i = 0; i < 40; i++){
+ etch_tcpconx_close(cxclient[i],0);
+ }
+
+ result = etch_tcpsvr_stop(tcp_server);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ CU_ASSERT_EQUAL_FATAL(tcp_server->is_started, FALSE);
+ CU_ASSERT_EQUAL_FATAL(tcp_server->state, ETCH_TCPSERVER_STATE_STOPPED);
+
+ result = etch_tcpsvr_close(tcp_server);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* free items created here */
+ etch_object_destroy(cxclient_last);
+
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * on_data_server_test_write_to_server()
+ * server on_data overide for the test_write_to_server() test
+ */
+static int on_data_server_test_write_to_server(void* data, const int unused, int len, void* bufferData)
+{
+ etch_tcp_connection* c = (etch_tcp_connection*)data;
+ etch_flexbuffer* fbuf = (etch_flexbuffer*)bufferData;
+ int result = 0;
+ char* datacopy = 0;
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "conxn %d on_data server test overide got %d bytes ...\n",
+ c->cx.conxid, len);
+ datacopy = etch_malloc(len+2,0);
+ memcpy(datacopy, fbuf->buf, len);
+ datacopy[len] = '\n'; datacopy[len+1] = '\0';
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, datacopy);
+ etch_free(datacopy);
+ return result;
+}
+
+
+/**
+ * test_write_to_server()
+ * test that we can send a message to a tcp server.
+ */
+static void test_write_to_server(void)
+{
+ int result = 0;
+ int arc = 0;
+ etch_tcp_connection* cxclient = 0;
+
+ etch_tcp_server* tcp_server = setup_a_test();
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tcp_server);
+
+ /* this test overrides server's accepted connection data handler */
+ tcp_server->on_data = on_data_server_test_write_to_server;
+
+ result = etch_tcpsvr_open(tcp_server, 0);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpsvr_start(tcp_server);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ etch_sleep(500); /* not needed, used for watching console */
+
+ cxclient = new_tcp_connection(g_test_url, NULL, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient);
+
+ result = etch_tcpconx_open(cxclient, FALSE);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ /* send data to server */
+ etch_tcpclient_send(cxclient, (unsigned char*)g_senddata1, g_buflen1, &arc);
+
+ /* if omit this sleep, server receives both sends as one,
+ * either way is is fine */
+ etch_sleep(200);
+
+ /* send more data to server */
+ etch_tcpclient_send(cxclient, (unsigned char*)g_senddata2, g_buflen2, &arc);
+
+ etch_sleep(500); /* not needed, used for watching console */
+
+ result = etch_tcpconx_close(cxclient, FALSE);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ /* test that we can omit an explicit server stop */
+ result = etch_tcpsvr_close(tcp_server);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* free items created here */
+ etch_object_destroy(cxclient);
+
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+#if 0
+/*
+ * on_data_server_test_write_with_readback()
+ * receive data handler for server side of test_write_with_readback
+ * this is not executed on the main thread, so no CU_ASSERTs can appear here.
+ * logs data received at the tcp server and sends it back to client
+ */
+static int on_data_server_test_write_with_readback(void* data, const int unused, int length, void* bufferData)
+{
+ etch_connection* cx = (etch_connection*)data;
+ etch_flexbuffer* fbuf = (etch_flexbuffer*)bufferData;
+ int result = 0;
+ char* datacopy = 0; int arc = 0;
+ etch_tcp_server* tcps = (etch_tcp_server*) cx->listener;
+ etch_tcp_connection* tcpx = tcps? tcps->cxlisten: NULL;
+ assert(is_etch_tcpserver(tcps));
+ assert(is_etch_tcpconnection(tcpx));
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG,
+ "connxn %d TEST SERVER ONDATA GOT %d bytes ...\n", cx->conxid, length);
+
+ datacopy = etch_malloc(length+2,0); memcpy(datacopy, fbuf->buf, length);
+ datacopy[length] = '\n'; datacopy[length+1] = '\0';
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, datacopy);
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG,
+ "conxn %d TEST SERVER ONDATA RESENDING to client \n", tcpx->cx.conxid);
+ etch_tcpclient_send(tcpx, fbuf->buf, length, &arc); /* return to sender */
+ etch_free(datacopy);
+ return result;
+}
+
+
+/*
+ * on_data_client_test_write_with_readback()
+ * receive data handler for client side of test_write_with_readback
+ * just logs the data received back from tcp server
+ */
+static int on_data_client_test_write_with_readback (etch_tcp_connection* c, const int unused, int length, char* data)
+{
+ int result = 0;
+ char* datacopy = 0;
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG,
+ "connxn %d TEST CLIENT ONDATA GOT %d bytes ...\n", c->cx.conxid, length);
+ datacopy = etch_malloc(length+2,0); memcpy(datacopy, data, length);
+ datacopy[length] = '\n'; datacopy[length+1] = '\0';
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, datacopy);
+ etch_free(datacopy);
+ return result;
+}
+
+
+/**
+ * test_write_with_readback()
+ * test that we can send data to server and that server can send it back to us
+ */
+static void test_write_with_readback(void)
+{
+ char rcvbuf[RCVBUFLEN];
+ int result = 0, datalen = 0, sndrc = 0, rcvrc = 0;
+ etch_tcp_connection *cxclient = 0;
+ etch_tcp_server *tcp_server = 0;
+ etch_tcp_client *tcp_client = 0;
+
+ /*
+ * create and start a tcp server
+ */
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "CREATING SERVER\n");
+ tcp_server = setup_a_test();
+ /* override server's accepted socket data handler to be our test handler */
+ tcp_server->on_data = on_data_server_test_write_with_readback;
+
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "STARTING SERVER\n");
+ result = etch_tcpsvr_open(tcp_server, 0);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpsvr_start(tcp_server);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ /*
+ * open a client connection and listener
+ */
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "CREATING CLIENT CONNECTION\n");
+ cxclient = new_tcp_connection(g_test_url, NULL, NULL);
+ cxclient->cx.on_data = on_data_client_test_write_with_readback;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient);
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "client connection ID is %d\n", cxclient->cx.conxid);
+
+ /* opening this client connection results in an accepted connection on our server.
+ * that accepted connection will recognize and use our test data handler, since we
+ * overrode that handler above. */
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "OPENING CLIENT CONNECTION\n");
+ result = etch_tcpconx_open (cxclient, FALSE);
+ etch_sleep(100);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ /* start a client side listener for the client socket - this is normally done
+ * via a start/waitup mechanism, however for the unit test we do so explicitly here.
+ * note that the tcp_client class is normally not visible, we expose it for the test. */
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "STARTING CLIENT LISTENER THREAD\n");
+ result = etch_tcpclient_start_listener (cxclient);
+ etch_sleep(100);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ tcp_client = cxclient->rcvlxr; /* check the mutual tcp connection references */
+ CU_ASSERT_PTR_EQUAL_FATAL(cxclient, tcp_client->cxlisten);
+
+ /*
+ * send data to server and receive data back from server
+ * note that both server and client data handlers are overridden above
+ */
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "SENDING DATA TO SERVER\n");
+ etch_tcpclient_send(cxclient, g_senddata1, g_buflen1, &sndrc);
+ datalen = etch_tcpclient_receive(cxclient, rcvbuf, RCVBUFLEN, &rcvrc);
+
+ CU_ASSERT_EQUAL(datalen, g_buflen1); /* verify sent == received */
+ CU_ASSERT_EQUAL(0, memcmp(rcvbuf, g_senddata1, datalen));
+
+ /*
+ * send more data to server and receive data back from server
+ */
+ //ETCH_LOG("TEST", ETCH_LOG_DEBUG, "SENDING MORE DATA TO SERVER\n");
+ //etch_tcpclient_send(cxclient, g_senddata2, g_buflen2, &sndrc);
+ //datalen = etch_tcpclient_receive(cxclient, rcvbuf, RCVBUFLEN, &rcvrc);
+
+ //CU_ASSERT_EQUAL(datalen, g_buflen2); /* verify sent == received */
+ //CU_ASSERT_EQUAL(0, memcmp(rcvbuf, g_senddata2, datalen));
+
+ /* stop the client side listener thread - this is normally done via a
+ * stop/waitdown mechanism, however for the unit test we do so explicitly here */
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "STOPPING CLIENT LISTENER THREAD\n");
+ result = etch_tcpclient_stop_listener (cxclient);
+ etch_sleep(100);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ /*
+ * close client connection
+ */
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "CLOSING CLIENT CONNECTION\n");
+ result = etch_tcpconx_close(cxclient, FALSE);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ /*
+ * shut down tcp server
+ */
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "STOPPING SERVER\n");
+ result = etch_tcpsvr_stop(tcp_server); /* could omit this */
+ CU_ASSERT_EQUAL(result, 0);
+
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "CLOSING SERVER\n");
+ result = etch_tcpsvr_close(tcp_server);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /*
+ * free memory
+ */
+ etch_object_destroy(cxclient);
+
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+#endif
+
+#if 0
+/**
+ * test_multiple_client_write_with_readback()
+ * test that two clients can send data to server and receive it back.
+ */
+static void test_multiple_client_write_with_readback(void)
+{
+ char rcvbuf[RCVBUFLEN];
+ int result = 0, datalen = 0, sndrc = 0, rcvrc = 0;
+ etch_tcp_connection *cxclient_a = 0, *cxclient_b = 0;
+ etch_tcp_server* tcp_server = 0;
+
+ /*
+ * create and start a tcp server
+ */
+ tcp_server = setup_a_test();
+ tcp_server->on_data = on_data_server_test_write_with_readback;
+
+ result = etch_tcpsvr_open(tcp_server, 0);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+
+ result = etch_tcpsvr_start(tcp_server);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ etch_sleep(500);
+
+ /*
+ * open multiple client connections
+ */
+ cxclient_a = new_tcp_connection(g_test_url, NULL, NULL);
+ cxclient_a->cx.on_data = on_data_client_test_write_with_readback;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient_a);
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "client a id is %d\n", cxclient_a->cx.conxid);
+
+ cxclient_b = new_tcp_connection(g_test_url, NULL, NULL);
+ cxclient_b->cx.on_data /* override data handler */
+ = on_data_client_test_write_with_readback;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(cxclient_b);
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "client b id is %d\n", cxclient_b->cx.conxid);
+
+ result = etch_tcpconx_open(cxclient_a, FALSE);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ result = etch_tcpconx_open(cxclient_b, FALSE);
+ CU_ASSERT_EQUAL_FATAL(result, 0);
+ etch_sleep(500);
+
+ /*
+ * client a send data to server and receive data back from server
+ */
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "client a sending data\n", cxclient_b->cx.conxid);
+ etch_tcpclient_send(cxclient_a, g_senddata1, g_buflen1, &sndrc);
+ datalen = etch_tcpclient_receive(cxclient_a, rcvbuf, RCVBUFLEN, &rcvrc);
+
+ CU_ASSERT_EQUAL(datalen, g_buflen1); /* verify sent == received */
+ CU_ASSERT_EQUAL(0, memcmp(rcvbuf, g_senddata1, datalen));
+
+ /*
+ * client b send data to server and receive data back from server
+ */
+ ETCH_LOG("TEST", ETCH_LOG_DEBUG, "client b sending data\n", cxclient_b->cx.conxid);
+ etch_tcpclient_send(cxclient_b, g_senddata2, g_buflen2, &sndrc);
+ datalen = etch_tcpclient_receive(cxclient_b, rcvbuf, RCVBUFLEN, &rcvrc);
+
+ CU_ASSERT_EQUAL(datalen, g_buflen2); /* verify sent == received */
+ CU_ASSERT_EQUAL(0, memcmp(rcvbuf, g_senddata2, datalen));
+ etch_sleep(500);
+
+ /*
+ * close client connections
+ */
+ result = etch_tcpconx_close(cxclient_a, FALSE);
+ CU_ASSERT_EQUAL(result, 0);
+ result = etch_tcpconx_close(cxclient_b, FALSE);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /*
+ * shut down tcp server
+ */
+ result = etch_tcpsvr_close(tcp_server);
+ CU_ASSERT_EQUAL(result, 0);
+
+ /*
+ * free memory
+ */
+ etch_object_destroy(cxclient_a);
+ etch_object_destroy(cxclient_b);
+
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+#endif
+
+
+#if 0
+/**
+ * test_receive_with_timeout()
+ * test that we can time out a receive operation
+ */
+static void test_receive_with_timeout(void)
+{
+ char rcvbuf[RCVBUFLEN];
+ int result = 0, datalen = 0, sndrc = 0, rcvrc = 0;
+ etch_tcp_connection* cxclient = 0;
+ etch_tcp_server* tcp_server = 0;
+
+ tcp_server = setup_a_test();
+ tcp_server->on_data = on_data_server_test_write_with_readback;
+ result = etch_tcpsvr_open (tcp_server, 0);
+ result = etch_tcpsvr_start(tcp_server);
+
+ cxclient = new_tcp_connection(g_test_url, NULL, NULL);
+ result = etch_tcpconx_open(cxclient, FALSE);
+
+ /* do one send and two receives, the 2nd with 2-second timeout */
+ etch_tcpclient_send(cxclient, g_senddata1, g_buflen1, &sndrc);
+ datalen = etch_tcpclient_receive (cxclient, rcvbuf, RCVBUFLEN, &rcvrc);
+ datalen = etch_tcpclient_receivex(cxclient, rcvbuf, RCVBUFLEN, 2000, &rcvrc);
+ CU_ASSERT_EQUAL(IS_ETCH_SOCKET_TIMEOUT(rcvrc), TRUE);
+
+ result = etch_tcpconx_close(cxclient, FALSE);
+ result = etch_tcpsvr_close(tcp_server);
+ etch_object_destroy(cxclient);
+ teardown_a_test(tcp_server);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+#endif
+
+/**
+ * main
+ */
+//int _tmain(int argc, _TCHAR* argv[])
+CU_pSuite test_etch_tcpconn_suite()
+{
+ CU_pSuite ps = CU_add_suite("suite_tcpconn", init_suite, clean_suite);
+
+ CU_add_test(ps, "test max number of connections", test_max_connections);
+
+ CU_add_test(ps, "test create and destroy tcp server", test_server_create_destroy);
+ CU_add_test(ps, "test test setup and teardown", test_setup_a_test);
+ CU_add_test(ps, "test tcp server open/close", test_server_open_close);
+ CU_add_test(ps, "test tcp server start/stop", test_server_start_stop);
+
+ CU_add_test(ps, "test server control start/stop", test_server_control_start_stop);
+ CU_add_test(ps, "test server control wait up/down", test_server_control_waitup_waitdown);
+
+ CU_add_test(ps, "test connect to server", test_connect_to_server);
+ CU_add_test(ps, "test multiple connect to server", test_multiple_connect_to_server);
+ CU_add_test(ps, "test write to server", test_write_to_server);
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * these last three tests used to work but are now broken.
+ * all tests that return data from server to client no longer work.
+ * the re-send of data back to client finds "socket not connected" .
+ * the production mailbox stuff works, so not sure what the problem is
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+ // CU_add_test(ps, "test write with readback", test_write_with_readback);
+ // CU_add_test(ps, "test multiple client write/read", test_multiple_client_write_with_readback);
+ // CU_add_test(ps, "test receive with timeout", test_receive_with_timeout);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/transport/test_threadpool.c b/binding-c/runtime/c/src/test/transport/test_threadpool.c
new file mode 100644
index 0000000..e039229
--- /dev/null
+++ b/binding-c/runtime/c/src/test/transport/test_threadpool.c
@@ -0,0 +1,1417 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_threadpool.c
+ */
+#include "etch_runtime.h"
+#include "etch_thread.h"
+#include "etch_mutex.h"
+#include "etch_wait.h"
+#include "etch_arraylist.h"
+#include "etch_mem.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE FALSE
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+static etch_mutex* mutex_threadid;
+static int g_next_threadid;
+
+#define IS_DUMP_AFTER_REMOVE FALSE
+#define IS_TESTING_REMOVE TRUE
+#define THREAD_TESTDATA_SIG 0xfeedface
+struct usertestdata { int x; int y; };
+
+static int next_threadid()
+{
+ etch_mutex_lock(mutex_threadid);
+ ++g_next_threadid;
+ etch_mutex_unlock(mutex_threadid);
+ return g_next_threadid;
+}
+
+/*
+ * get_threaddata()
+ * get a handle to internal thread create parameters
+ */
+static etch_apr_threaddata* get_threaddata()
+{
+ static etch_apr_threaddata tdata;
+ memset(&tdata, 0, sizeof(etch_apr_threaddata));
+ return &tdata;
+}
+
+
+/**
+ * on_threadstart()
+ * thread start callback
+ */
+static int on_threadstart(void* param)
+{
+ #if (IS_DEBUG_CONSOLE)
+ if (NULL == param)
+ printf("pool thread start\n");
+ else printf("pool thread %04x start\n", params->etch_thread_id);
+ fflush(stdout);
+ #endif
+ return 0;
+}
+
+
+/**
+ * on_threadexit()
+ * thread stop callback
+ */
+static int on_threadexit(void* param)
+{
+ #if (IS_DEBUG_CONSOLE)
+ if (NULL == param)
+ printf("pool thread exit\n");
+ else printf("pool thread %04x exit\n", params->etch_thread_id);
+ fflush(stdout);
+ #endif
+ return 0;
+}
+
+
+/**
+ * createthread_threadproc
+ */
+static void createthread_threadproc(void* data)
+{
+ etch_thread_params* params;
+ struct usertestdata* ud;
+ #if (IS_DEBUG_CONSOLE)
+ printf("in createthread_threadproc\n");
+ #endif
+
+ params = (etch_thread_params*) data;
+ ud = (struct usertestdata*) params->data;
+ CU_ASSERT_PTR_NOT_NULL_FATAL(ud);
+ CU_ASSERT_EQUAL(ud->x, 1);
+ CU_ASSERT_EQUAL(ud->y, 2);
+}
+
+
+/**
+ * test_createthread
+ */
+static void test_createthread(void)
+{
+ int result = 0;
+ etch_thread_params tp;
+ struct usertestdata ud;
+ etch_apr_threaddata* td = get_threaddata();
+ memset(&tp, 0, sizeof(etch_thread_params));
+ ud.x = 1; ud.y = 2;
+
+ // TODO: pool
+ etch_mutex_create(&mutex_threadid, ETCH_MUTEX_NESTED, NULL);
+
+ etch_init_threadparams(&tp);
+ tp.etch_thread_id = next_threadid();
+ tp.data = &ud;
+ tp.libdata = td;
+ tp.on_start = on_threadstart;
+ tp.on_exit = on_threadexit;
+ tp.threadproc = createthread_threadproc;
+ #if (IS_DEBUG_CONSOLE)
+ printf("\n");
+ #endif
+
+ result = etch_createthread(&tp);
+ CU_ASSERT_EQUAL(result, 0);
+
+ etch_thread_join(&tp); /* block until thread exit */
+ CU_ASSERT_EQUAL(result, 0);
+
+ etch_mutex_destroy(mutex_threadid);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * mutextestparams
+ * thread data for mutex test
+ */
+struct mutextestparams
+{
+ unsigned signature;
+ etch_mutex* mutex;
+ unsigned flag;
+};
+
+
+/**
+ * mutex_threadproc_a
+ */
+static void mutex_threadproc_a(void* data)
+{
+ etch_status_t status;
+ int result = 0;
+ etch_thread_params* params;
+ struct mutextestparams* p;
+ params = (etch_thread_params*) data;
+ p = (struct mutextestparams*) params->data;
+ #if (IS_DEBUG_CONSOLE)
+ printf("in threadproc_a\n"); fflush(stdout);
+ #endif
+
+ /* can't use a FATAL cunit macro outside the main thread
+ * since if it fails the exit handling causes an OS fault */
+
+ CU_ASSERT_PTR_NOT_NULL(p); if (!p) return;
+ CU_ASSERT_EQUAL(p->signature, THREAD_TESTDATA_SIG);
+ if (p->signature != THREAD_TESTDATA_SIG) return;
+ CU_ASSERT_PTR_NOT_NULL(p->mutex); if (!p->mutex) return;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread a waiting ...\n"); fflush(stdout);
+ #endif
+
+ while(p->flag == 0) /* wait until ready to acquire lock */
+ apr_sleep(1 * 1000 * 1000);
+
+ CU_ASSERT_EQUAL(p->flag, 1); if (p->flag != 1) return;
+
+ etch_mutex_lock(p->mutex);
+ CU_ASSERT_EQUAL(result, 0); if (result != 0) return;
+
+ p->flag = 2; /* signal thread b to attempt acquire */
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread a waiting ...\n"); fflush(stdout);
+ #endif
+
+ while(p->flag == 2) /* wait for thread thread b signal */
+ apr_sleep(1 * 1000 * 1000);
+
+ status = etch_mutex_unlock(p->mutex);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ p->flag = 4; /* signal thread b to proceed */
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread a waiting ...\n"); fflush(stdout);
+ #endif
+
+ while(p->flag == 4) /* wait until thread b signals done */
+ apr_sleep(1 * 1000 * 1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread s exits\n"); fflush(stdout);
+ #endif
+}
+
+
+/**
+ * mutex_threadproc_b
+ */
+static void mutex_threadproc_b(void* data)
+{
+ etch_status_t status;
+ etch_thread_params* params;
+ struct mutextestparams* p;
+ params = (etch_thread_params*) data;
+ p = (struct mutextestparams*) params->data;
+ #if (IS_DEBUG_CONSOLE)
+ printf("in mutex_threadproc_b\n"); fflush(stdout);
+ #endif
+
+ CU_ASSERT_PTR_NOT_NULL(p);
+ CU_ASSERT_EQUAL(p->signature, THREAD_TESTDATA_SIG);
+ CU_ASSERT_PTR_NOT_NULL(p->mutex);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b waiting ...\n"); fflush(stdout);
+ #endif
+
+ while(p->flag != 2) /* wait until ready to attempt lock */
+ apr_sleep(1 * 1000 * 1000);
+
+ status = etch_mutex_trylock(p->mutex);
+ CU_ASSERT_NOT_EQUAL(status, ETCH_SUCCESS);
+
+ p->flag = 3; /* signal thread a to continue */
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b waiting ...\n"); fflush(stdout);
+ #endif
+
+ while(p->flag == 3) /* wait for thread a to release lock */
+ apr_sleep(1 * 1000 * 1000);
+
+ status = etch_mutex_trylock(p->mutex);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ status = etch_mutex_unlock(p->mutex);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ p->flag = 5; /* signal thread a that we are done here */
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b waiting 2 secs ...\n"); fflush(stdout);
+ #endif
+
+ /* ensure thread b is last to exit for simplicity */
+ apr_sleep(2 * 1000 * 1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread_b exits\n"); fflush(stdout);
+ #endif
+}
+
+
+/**
+ * test_mutex()
+ * launches two threads which share a mutex and a signal flag with the main thread.
+ * 1. thread a sets the lock, signals thread b, and waits.
+ * 2. thread b tries to set the lock, fails as expected, signals thread a, and waits.
+ * 3. thread a releases the lock, signals thread b, and waits.
+ * 4. thread b tries the lock, succeeds as expected, releases the lock, signals thread a.
+ * 5. thread a exits
+ * 6. thread b exits
+ */
+static void test_mutex(void)
+{
+ etch_status_t status;
+ int result = 0;
+ etch_mutex* mutex = 0;
+ etch_apr_threaddata* td = get_threaddata();
+ etch_thread_params tpa, tpb;
+ struct mutextestparams threadparams;
+ etch_init_threadparams(&tpa);
+ etch_init_threadparams(&tpb);
+
+ // TODO: pool
+ etch_mutex_create(&mutex_threadid, ETCH_MUTEX_NESTED, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mutex_threadid);
+
+ // TODO: pool
+ etch_mutex_create(&mutex, ETCH_MUTEX_NESTED, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mutex);
+
+ status = etch_mutex_lock(mutex);
+ CU_ASSERT_EQUAL_FATAL(status, ETCH_SUCCESS);
+
+ // result = mutex->tryacquire(mutex); /* mutexes are nestable */
+ // CU_ASSERT_NOT_EQUAL_FATAL(result, 0);
+ status = etch_mutex_unlock(mutex);
+ CU_ASSERT_EQUAL_FATAL(status, ETCH_SUCCESS);
+
+ threadparams.mutex = mutex;
+ threadparams.flag = 0;
+ threadparams.signature = THREAD_TESTDATA_SIG;
+
+ tpa.etch_thread_id = next_threadid();
+ tpa.libdata = td;
+ tpa.on_start = on_threadstart;
+ tpa.on_exit = on_threadexit;
+ tpa.threadproc = mutex_threadproc_a;
+ tpa.data = &threadparams;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\n");
+ #endif
+
+ result = etch_createthread(&tpa); /* create thread a */
+ CU_ASSERT_EQUAL(result, 0);
+
+ memcpy(&tpb, &tpa, sizeof(etch_thread_params));
+ tpb.etch_thread_id = next_threadid();
+ tpb.threadproc = mutex_threadproc_b;
+
+ result = etch_createthread(&tpb); /* create thread b */
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* signal the new threads to commence doing something */
+ threadparams.flag = 1;
+
+ etch_thread_join(&tpa); /* wait for thread a exit */
+ etch_thread_join(&tpb); /* wait for thread b exit */
+
+ etch_mutex_destroy(mutex);
+ etch_mutex_destroy(mutex_threadid);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * synchedlist_testparams
+ * thread data for synched list test
+ */
+typedef struct synchedlist_testparams
+{
+ unsigned signature;
+ etch_arraylist* list;
+ etch_mutex* mutex;
+ uint16 flag;
+} synchedlist_testparams;
+
+/*
+static void dumplist(etch_arraylist* list)
+{
+ int i=0;
+ char result;
+ const int n = list->count;
+ result = etch_arraylist_trylock(list);
+ if (result != 0) return;
+ // printf("\ndumping list ...\n"); fflush(stdout);
+ for(; i < n; i++)
+ { etch_int32* item = list->base[i];
+ printf("\n list[%d] %d\n", i, item->value); fflush(stdout);
+ }
+
+ result = etch_arraylist_rellock(list);
+ // printf("\nany key ..."); while(!c) c = _getch(); printf("\n");
+}
+*/
+
+/*
+ * synched_arraylist_comparator()
+ * comparator to compare an integer constant with an etch_int32.
+ */
+static int synched_arraylist_comparator(void* testvalue, void* listcontent)
+{
+ const int ivalue = (int) (size_t) testvalue;
+ etch_int32* listobj = (etch_int32*) listcontent;
+ int jvalue = listobj->value;
+ return ivalue < jvalue? -1: ivalue > jvalue? 1: 0;
+}
+
+
+/**
+ * synchedlist_threadproc_a
+ */
+static void synchedlist_threadproc_a(void* data)
+{
+ int result = 0, i;
+ uint16 flag = 0;
+ etch_iterator iterator;
+ etch_thread_params* params;
+ synchedlist_testparams* p;
+ params = (etch_thread_params*) data;
+ p = (struct synchedlist_testparams*) params->data;
+ #if (IS_DEBUG_CONSOLE)
+ printf("in threadproc_a\n"); fflush(stdout);
+ #endif
+
+ /* can't use a FATAL cunit macro outside the main thread
+ * since if it fails the exit handling causes an OS fault */
+
+ CU_ASSERT_PTR_NOT_NULL(p); if (!p) return;
+ CU_ASSERT_EQUAL(p->signature, THREAD_TESTDATA_SIG);
+ if (p->signature != THREAD_TESTDATA_SIG) return;
+ CU_ASSERT_PTR_NOT_NULL(p->list); if (!p->list) return;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread a waiting ...\n"); fflush(stdout);
+ #endif
+
+ /* wait until main signals start */
+ do {
+ etch_mutex_lock(p->mutex);
+ flag = p->flag;
+ etch_mutex_unlock(p->mutex);
+ apr_sleep(1 * 500 * 1000);
+ } while(flag < 2);
+
+ CU_ASSERT_TRUE(p->flag >= 2);
+ if (p->flag < 2) {
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread a p->flag < 2 is false ...\n"); fflush(stdout);
+ #endif
+ return;
+ }
+
+ /* both threads should be contending for the list now */
+ /* wait until main signals start */
+ for(i = 1; i <= 20; i++)
+ {
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread a arraylist_add ...\n"); fflush(stdout);
+ #endif
+ etch_arraylist_add(p->list, new_int32(i));
+ etch_thread_yield();
+ }
+
+ etch_mutex_lock(p->mutex);
+ p->flag++;
+ etch_mutex_unlock(p->mutex);
+ do {
+ etch_mutex_lock(p->mutex);
+ flag = p->flag;
+ etch_mutex_unlock(p->mutex);
+ apr_sleep(1 * 500 * 1000);
+ } while(flag < 3);
+
+
+ CU_ASSERT_EQUAL(p->list->count, 40);
+
+ etch_mutex_lock(p->mutex);
+ p->flag++;
+ etch_mutex_unlock(p->mutex);
+ do {
+ etch_mutex_lock(p->mutex);
+ flag = p->flag;
+ etch_mutex_unlock(p->mutex);
+ apr_sleep(1 * 500 * 1000);
+ } while(flag < 5);
+
+ #if IS_TESTING_REMOVE
+
+ /* both threads should be contending for the list now */
+ for(i = 4; i <= 20; i += 4)
+ {
+ /* get the list index for value i */
+ const int index = etch_arraylist_indexof(p->list, (void*)(size_t) i, 0, synched_arraylist_comparator);
+ CU_ASSERT_NOT_EQUAL(index, -1);
+
+ if (index >= 0)
+ {
+ result = etch_arraylist_remove(p->list, index, TRUE);
+ CU_ASSERT_EQUAL(result, 0);
+
+ #if (IS_DEBUG_CONSOLE)
+ if (result == 0)
+ printf("thread a arraylist_remove index %d value %d\n", index, i);
+ else printf("thread a could not remove index %d value %d\n", index, i);
+ fflush(stdout);
+ #endif
+ }
+
+ etch_thread_yield();
+ }
+
+ #endif /* IS_TESTING_REMOVE */
+
+ p->flag++;
+ while(p->flag < 7) /* wait until both threads are done removing from list */
+ apr_sleep(1 * 1000 * 500);
+
+ #if IS_TESTING_REMOVE
+ CU_ASSERT_EQUAL(p->list->count, 30); /* each thread removed 5 items */
+ #endif
+
+ etch_mutex_lock(p->mutex);
+ p->flag++;
+ etch_mutex_unlock(p->mutex);
+ do {
+ etch_mutex_lock(p->mutex);
+ flag = p->flag;
+ etch_mutex_unlock(p->mutex);
+ apr_sleep(1 * 500 * 1000);
+ } while(flag < 9);
+
+ set_iterator(&iterator, p->list, &p->list->iterable);
+
+ result = etch_arraylist_getlock(p->list);
+ CU_ASSERT_EQUAL(result, 0);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread a iterating list ...\n"); fflush(stdout);
+ #endif
+
+ while(iterator.has_next(&iterator))
+ {
+ etch_int32* item = (etch_int32*) iterator.current_value;
+ CU_ASSERT_PTR_NOT_NULL(item);
+
+ if (item)
+ { result = item->value;
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread a list value %d\n", result); fflush(stdout);
+ #endif
+ #if IS_TESTING_REMOVE
+ CU_ASSERT_NOT_EQUAL(result % 4, 0); /* we removed all modulo 4 values */
+ #endif
+ }
+
+ result = iterator.next(&iterator);
+ }
+
+ etch_sleep(1000);
+ result = etch_arraylist_rellock(p->list);
+
+
+ etch_mutex_lock(p->mutex);
+ p->flag++;
+ etch_mutex_unlock(p->mutex);
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread a waiting to exit ...\n"); fflush(stdout);
+ #endif
+ do {
+ etch_mutex_lock(p->mutex);
+ flag = p->flag;
+ etch_mutex_unlock(p->mutex);
+ apr_sleep(1 * 500 * 1000);
+ } while(flag < 11);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread a exits\n"); fflush(stdout);
+ #endif
+}
+
+
+/**
+ * synchedlist_threadproc_b
+ */
+static void synchedlist_threadproc_b(void* data)
+{
+ int result = 0, i;
+ uint16 flag = 0;
+ etch_iterator iterator;
+ etch_thread_params* params;
+ struct synchedlist_testparams* p;
+ params = (etch_thread_params*) data;
+ p = (struct synchedlist_testparams*) params->data;
+ #if (IS_DEBUG_CONSOLE)
+ printf("in threadproc_b\n"); fflush(stdout);
+ #endif
+
+ /* can't use a FATAL cunit macro outside the main thread
+ * since if it fails the exit handling causes an OS fault */
+
+ CU_ASSERT_PTR_NOT_NULL(p); if (!p) return;
+ CU_ASSERT_EQUAL(p->signature, THREAD_TESTDATA_SIG);
+ if (p->signature != THREAD_TESTDATA_SIG) return;
+ CU_ASSERT_PTR_NOT_NULL(p->list); if (!p->list) return;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b waiting ...\n"); fflush(stdout);
+ #endif
+
+ /* wait until main signals start */
+ do {
+ etch_mutex_lock(p->mutex);
+ flag = p->flag;
+ etch_mutex_unlock(p->mutex);
+ apr_sleep(1 * 500 * 1000);
+ } while(flag == 0);
+
+ CU_ASSERT_EQUAL(p->flag, 1); if (p->flag != 1) return;
+
+ /* both threads should be contending for the list now */
+ for(i = 21; i <= 40; i++)
+ {
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b arraylist_add ...\n"); fflush(stdout);
+ #endif
+ etch_arraylist_add(p->list, new_int32(i));
+ etch_thread_yield();
+ }
+
+ p->flag++;
+ while(p->flag < 3) /* wait until both threads are done adding to list */
+ apr_sleep(1 * 1000 * 500);
+
+ CU_ASSERT_EQUAL(p->list->count, 40);
+ p->flag++;
+ while(p->flag < 5) /* wait until both threads are done checking list */
+ apr_sleep(1 * 1000 * 500);
+
+ #if IS_TESTING_REMOVE
+
+ /* both threads should be contending for the list now */
+ for(i = 24; i <= 40; i += 4)
+ {
+ const int index = etch_arraylist_indexof(p->list, (void*)(size_t) i, 0, synched_arraylist_comparator);
+ CU_ASSERT_NOT_EQUAL(index, -1);
+
+ if (index >= 0)
+ {
+ result = etch_arraylist_remove(p->list, index, TRUE);
+ CU_ASSERT_EQUAL(result, 0);
+
+ #if (IS_DEBUG_CONSOLE)
+ if (result == 0)
+ printf("thread b arraylist_remove index %d value %d\n", index, i);
+ else printf("thread b could not remove index %d value %d\n", index, i);
+ fflush(stdout);
+ #endif
+ }
+
+ etch_thread_yield();
+ }
+
+ #endif /* #if IS_TESTING_REMOVE */
+
+ p->flag++;
+ while(p->flag < 7) /* wait until both threads are done removing from list */
+ apr_sleep(1 * 1000 * 500);
+
+ #if IS_TESTING_REMOVE
+ CU_ASSERT_EQUAL(p->list->count, 30); /* each thread removed 5 items */
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b list count after remove is %d\n", p->list->count); fflush(stdout);
+ #endif /* IS_DEBUG_CONSOLE */
+ #endif /* IS_TESTING_REMOVE */
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b waiting for list lock ...\n"); fflush(stdout);
+ #endif /* IS_DEBUG_CONSOLE */
+ p->flag++;
+ while(p->flag < 9) /* wait until both threads are done checking list */
+ apr_sleep(1 * 1000 * 500);
+
+ set_iterator(&iterator, p->list, &p->list->iterable);
+
+ result = etch_arraylist_getlock(p->list);
+ CU_ASSERT_EQUAL(result, 0);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b iterating list ...\n"); fflush(stdout);
+ #endif /* IS_DEBUG_CONSOLE */
+
+ while(iterator.has_next(&iterator))
+ {
+ etch_int32* item = (etch_int32*) iterator.current_value;
+ CU_ASSERT_PTR_NOT_NULL(item);
+
+ if (item)
+ { result = item->value;
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b list value %d\n", result); fflush(stdout);
+ #endif
+ #if IS_TESTING_REMOVE
+ CU_ASSERT_NOT_EQUAL(result % 4, 0); /* we removed all modulo 4 values */
+ #endif
+ }
+
+ iterator.next(&iterator);
+ }
+
+ result = etch_arraylist_rellock(p->list);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b waiting to exit ...\n"); fflush(stdout);
+ #endif
+
+ p->flag++;
+ while(p->flag < 11) /* wait until both threads are done iterating list */
+ apr_sleep(1 * 1000 * 500);
+
+ /* ensure thread b is last to exit for simplicity */
+ apr_sleep(1 * 1000 * 1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread b exits\n"); fflush(stdout);
+ #endif
+}
+
+
+/**
+ * test_synched_arraylist()
+ * launches two threads which share a synchronized arraylist.
+ */
+static void test_synched_arraylist(void)
+{
+ int result = 0;
+ etch_mutex* mutex = 0;
+ synchedlist_testparams tparams;
+ etch_apr_threaddata* td = get_threaddata();
+ etch_thread_params tpa, tpb;
+ memset(&tpa, 0, sizeof(etch_thread_params));
+ memset(&tpb, 0, sizeof(etch_thread_params));
+
+ // TODO: pool
+ etch_mutex_create(&mutex_threadid, ETCH_MUTEX_NESTED, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mutex_threadid);
+
+ // TODO: pool
+ etch_mutex_create(&mutex, ETCH_MUTEX_NESTED, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mutex_threadid);
+
+ tparams.list = new_etch_arraylist(0,0);
+ ((etch_object*)tparams.list)->synclock = mutex; /* list now owns the mutex */
+ tparams.flag = 0;
+ tparams.signature = THREAD_TESTDATA_SIG;
+
+ // TODO: pool
+ etch_mutex_create(&tparams.mutex, ETCH_MUTEX_NESTED, NULL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(mutex_threadid);
+
+ tpa.etch_thread_id = ++g_next_threadid;
+ tpa.libdata = td;
+ tpa.on_start = on_threadstart;
+ tpa.on_exit = on_threadexit;
+ tpa.threadproc = synchedlist_threadproc_a;
+ tpa.data = &tparams;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\n");
+ #endif
+
+ result = etch_createthread(&tpa); /* create thread a */
+ CU_ASSERT_EQUAL(result, 0);
+
+ memcpy(&tpb, &tpa, sizeof(etch_thread_params));
+ tpb.etch_thread_id = ++g_next_threadid;
+ tpb.threadproc = synchedlist_threadproc_b;
+
+ result = etch_createthread(&tpb); /* create thread b */
+ CU_ASSERT_EQUAL(result, 0);
+
+ /* signal both threads to start doing something */
+ etch_mutex_lock(tparams.mutex);
+ tparams.flag = 1;
+ etch_mutex_unlock(tparams.mutex);
+
+ etch_thread_join(&tpb); /* wait for thread b exit */
+ etch_thread_join(&tpa); /* wait for thread a exit */
+
+ etch_object_destroy(tparams.list); /* destroy list and mutex */
+
+ etch_mutex_destroy(mutex_threadid);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+typedef struct waittestdata { int flag; int num; int is_disposable; } waittestdata;
+
+
+/**
+ * wait_threadproc_a
+ */
+static void wait_threadproc_a(void* data)
+{
+ etch_thread_params* params;
+ waittestdata* threadtestdata;
+ params = (etch_thread_params*) data;
+ threadtestdata = (waittestdata*) params->data;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nin wait thread_a\n"); fflush(stdout);
+ #endif
+
+ threadtestdata->flag = 1;
+
+ etch_sleep(3000);
+
+ if (threadtestdata->is_disposable)
+ etch_free(threadtestdata);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("exit wait thread_a\n"); fflush(stdout);
+ #endif
+}
+
+
+/**
+ * test_wait()
+ * test separate thread create and start
+ */
+static void test_wait(void)
+{
+ int result = 0;
+ etch_thread* thread = 0;
+ waittestdata* testdata = 0;
+ etch_thread_params* tp = 0;
+
+ testdata = etch_malloc(sizeof(waittestdata),0);
+ testdata->flag = testdata->num = 0;
+ testdata->is_disposable = FALSE;
+
+ /* create a thread that waits to be started */
+ thread = new_thread(wait_threadproc_a, testdata);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thread);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nnew thread waiting for signal coming in ... "); fflush(stdout);
+ #endif
+
+ tp = &thread->params;
+ /* testdata = tp->data; */
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("3 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("2 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("1 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nmain signaling new thread to start ...\n"); fflush(stdout);
+ #endif
+
+ thread->start(thread);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main issued newthread.start, waiting for exit ...\n"); fflush(stdout);
+ #endif
+
+ etch_sleep(1000);
+
+ etch_thread_join(tp); /* block until thread exit */
+
+ result = testdata->flag;
+ CU_ASSERT_EQUAL(result, 1);
+ etch_free(testdata);
+ etch_object_destroy(thread);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+typedef struct wait_until_testdata
+{
+ etch_wait_t* waiter; /* the condition variable waiter */
+ int64 waitingfor; /* value the waiter is waiting for */
+ int sleepstartms;
+ int sleepexitms;
+} wait_until_testdata;
+
+
+/**
+ * wait_until_threadproc_a
+ */
+static void wait_until_threadproc_a(void* data)
+{
+ etch_wait_t* waiter;
+ etch_thread_params* params;
+ wait_until_testdata* threadtestdata;
+ params = (etch_thread_params*) data;
+ threadtestdata = (wait_until_testdata*) params->data;
+ waiter = threadtestdata->waiter;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nthread: started ...\n", threadtestdata->sleepstartms); fflush(stdout);
+ #endif
+
+ etch_sleep(threadtestdata->sleepstartms); /* wait a bit before setting condition */
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nthread: setting unblock condition ...\n"); fflush(stdout);
+ #endif
+
+ etch_wait_set(waiter, threadtestdata->waitingfor);
+
+// waiter->set(waiter, threadtestdata->waitingfor); /* set wait condition to unblock waiters */
+
+ etch_sleep(threadtestdata->sleepexitms); /* wait a bit before exiting thread */
+
+ etch_free(threadtestdata); /* free caller's memory */
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("thread: exiting ...\n"); fflush(stdout);
+ #endif
+}
+
+
+/**
+ * test_wait_until()
+ * test wait on condition variable -- block until the specified condition is met.
+ */
+static void test_wait_until(void)
+{
+ etch_status_t status;
+ int result = 0;
+ etch_wait_t* waiter;
+ etch_thread* thread = 0;
+ etch_thread_params* tp = 0;
+ const int64 VALUE_TO_WAIT_FOR = 1;
+ wait_until_testdata* testdata = 0;
+
+ // TODO: pool
+ etch_wait_create(&waiter, NULL);
+
+ testdata = etch_malloc(sizeof(wait_until_testdata),0);
+ memset(testdata, 0, sizeof(wait_until_testdata));
+ testdata->waiter = waiter;
+ testdata->waitingfor = VALUE_TO_WAIT_FOR;
+ testdata->sleepstartms = testdata->sleepexitms = 2000;
+
+ /* create a thread that waits to be started */
+ thread = new_thread(wait_until_threadproc_a, testdata);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thread);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nmain: countdown to start worker thread ... "); fflush(stdout);
+ #endif
+
+ tp = &thread->params;
+ testdata = tp->data;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("3 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("2 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("1 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nmain: signaling worker thread to start ...\n"); fflush(stdout);
+ #endif
+
+ thread->start(thread);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main: worker thread started\n");
+ printf("main: start blocking on condition variable ...\n"); fflush(stdout);
+ #endif
+
+ /* wait for worker thread to set condition variable */
+ //result = waiter->timed_waitequal(waiter, &condvar, VALUE_TO_WAIT_FOR, 5000);
+ status = etch_wait_timedwait(waiter, VALUE_TO_WAIT_FOR, 5000);
+ switch(status) {
+ case ETCH_SUCCESS:
+ result = 0;
+ break;
+ case ETCH_ETIMEOUT:
+ result = ETCH_TIMEOUT;
+ break;
+ default:
+ result = -1;
+ break;
+ }
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main: wait unblocked, waiting for worker thread to exit ...\n"); fflush(stdout);
+ #endif
+ CU_ASSERT_NOT_EQUAL_FATAL(result, -1);
+ CU_ASSERT_NOT_EQUAL(result, ETCH_TIMEOUT);
+
+ etch_thread_join(tp); /* block until thread exit */
+
+ etch_wait_destroy(waiter);
+ etch_object_destroy(thread);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+
+/**
+ * test_wait_until_preexisting()
+ * test wait on condition variable when unblock condition exists prior to request.
+ */
+static void test_wait_until_preexisting(void)
+{
+ etch_status_t status;
+ int result = 0;
+ etch_wait_t* waiter;
+ etch_thread* thread = 0;
+ etch_thread_params* tp = 0;
+ const int64 VALUE_TO_WAIT_FOR = 1;
+ wait_until_testdata* testdata = 0;
+ clock_t tickcount1 = 0, tickcount2 = 0, tickdiff = 0;
+
+ // TODO: pool
+ etch_wait_create(&waiter, NULL);
+
+ testdata = etch_malloc(sizeof(wait_until_testdata),0);
+ memset(testdata, 0, sizeof(wait_until_testdata));
+ testdata->waiter = waiter;
+ testdata->waitingfor = VALUE_TO_WAIT_FOR;
+ testdata->sleepstartms = testdata->sleepexitms = 2000;
+
+ /* create a thread that waits to be started */
+ thread = new_thread(wait_until_threadproc_a, testdata);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thread);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nmain: countdown to start worker thread ... "); fflush(stdout);
+ #endif
+
+ tp = &thread->params;
+ testdata = tp->data;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("3 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("2 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("1 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nmain: signaling worker thread to start ...\n"); fflush(stdout);
+ #endif
+
+ thread->start(thread);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main: worker thread started\n");
+ #endif
+
+ //condvar = VALUE_TO_WAIT_FOR; /* pre-set unblock condition */
+ // TODO: check if this is correct
+ etch_wait_set(waiter, VALUE_TO_WAIT_FOR);
+ tickcount1 = clock();
+
+ /* wait for worker thread to set condition variable. we expect that the
+ * thread will not be started since the wait condition pre-exists */
+ status = etch_wait_timedwait(waiter, VALUE_TO_WAIT_FOR, 5000);
+ CU_ASSERT_EQUAL(status, ETCH_SUCCESS);
+
+ /* we expect zero wait time since the wait condition preexisted */
+ tickcount2 = clock();
+ tickdiff = tickcount2 - tickcount1;
+ result = tickdiff < 20; /* resolution of tick timer could be as high as 20ms */
+ CU_ASSERT_EQUAL(result, TRUE); /* if debugger stepping this assert will fail */
+
+ /* block until thread exit. however we expect that it has already done so. */
+ etch_thread_join(tp);
+
+ etch_wait_destroy(waiter);
+ etch_object_destroy(thread);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_wait_until_negative_test()
+ * test wait on condition variable
+ */
+static void test_wait_until_negative_test(void)
+{
+ etch_status_t status;
+ etch_wait_t* waiter;
+ etch_thread* thread = 0;
+ etch_thread_params* tp = 0;
+ const int VALUE_TO_WAIT_FOR = 1, VALUE_TO_SET = 2, THREAD_PAUSE_MS = 1000;
+ wait_until_testdata* testdata = 0;
+
+ // TODO: pool
+ etch_wait_create(&waiter, NULL);
+
+ testdata = etch_malloc(sizeof(wait_until_testdata),0);
+ memset(testdata, 0, sizeof(wait_until_testdata));
+ testdata->waiter = waiter;
+ testdata->waitingfor = VALUE_TO_SET; /* not the value we're going to wait for */
+ testdata->sleepstartms = testdata->sleepexitms = THREAD_PAUSE_MS;
+
+ /* create a thread that waits to be started */
+ thread = new_thread(wait_until_threadproc_a, testdata);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(thread);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nmain: countdown to start worker thread ... "); fflush(stdout);
+ #endif
+
+ tp = &thread->params;
+ testdata = tp->data;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("3 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("2 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("1 ... "); fflush(stdout);
+ #endif
+ etch_sleep(1000);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nmain: signaling worker thread to start ...\n"); fflush(stdout);
+ #endif
+
+ thread->start(thread);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main: worker thread started\n");
+ printf("main: start blocking on condition variable ...\n"); fflush(stdout);
+ #endif
+
+ /* wait for worker thread to set condition variable. in this test, we have told
+ * the thread to set some other value than what we are waiting for, so we want
+ * to see a wait timeout here. in other words, we are testing that setting the
+ * waiter to some value other than what we are waiting for, does not unblock.
+ */
+ status = etch_wait_timedwait(waiter, VALUE_TO_WAIT_FOR, THREAD_PAUSE_MS + 1000);
+ CU_ASSERT_EQUAL(status, ETCH_ETIMEOUT);
+ //result = waiter->timed_waitequal(waiter, &condvar, VALUE_TO_WAIT_FOR, THREAD_PAUSE_MS + 1000 );
+
+
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main: wait unblocked, waiting for worker thread to exit ...\n"); fflush(stdout);
+ #endif
+
+ etch_thread_join(tp); /* block until thread exit */
+
+ etch_wait_destroy(waiter);
+ etch_object_destroy(thread);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * wait_multiple_threadproc
+ */
+static void wait_multiple_threadproc(void* data)
+{
+ int threadnum, waitms, remainingms;
+ etch_thread_params* params = (etch_thread_params*) data;
+ waittestdata* tdata = (waittestdata*) params->data;
+ threadnum = tdata->num;
+ waitms = tdata->flag;
+ remainingms = waitms;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("\nwait thread %d sleeping %d ms ...\n", threadnum, waitms); fflush(stdout);
+ #endif
+
+ while(remainingms > 0)
+ {
+ if (params->threadstate > ETCH_THREADSTATE_STARTED) break;
+ etch_sleep(remainingms > 500? 500: remainingms);
+ remainingms -= 500;
+ }
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("exit wait thread %d\n", threadnum); fflush(stdout);
+ #endif
+}
+
+
+/**
+ * test_wait_for_multiple()
+ * launch multiple threads and wait for them all to complete
+ */
+static void test_wait_for_multiple(void)
+{
+ #define TEST_WAIT_MULT_NUMTHREADS 4
+
+ etch_thread* thread[TEST_WAIT_MULT_NUMTHREADS];
+ int waitms[TEST_WAIT_MULT_NUMTHREADS], i, priori = 1;
+
+ for(i=0; i < TEST_WAIT_MULT_NUMTHREADS; i++)
+ { /* initialize wait times for each thread */
+ const int newi = i & 1? priori - 1: priori + 2;
+ waitms[i] = ((priori = newi) * 1000);
+ }
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main creating all threads ...\n"); fflush(stdout);
+ #endif
+
+ for(i=0; i < TEST_WAIT_MULT_NUMTHREADS; i++) /* create all threads */
+ {
+ etch_thread* newthread;
+ waittestdata* thread_data = etch_malloc(sizeof(waittestdata),0);
+ thread_data->num = i; thread_data->flag = waitms[i];
+
+ newthread = new_thread(wait_multiple_threadproc, thread_data);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(newthread);
+
+ /* configure thread to free memory for the waittestdata we passed it */
+ newthread->params.is_own_data = TRUE;
+ thread[i] = newthread;
+ }
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main starting all threads ...\n"); fflush(stdout);
+ #endif
+
+ for(i=0; i < TEST_WAIT_MULT_NUMTHREADS; i++) /* start all threads */
+ thread[i]->start(thread[i]);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main waiting for all threads to exit ...\n"); fflush(stdout);
+ #endif
+
+ for(i=0; i < TEST_WAIT_MULT_NUMTHREADS; i++) /* wait for all threads to exit */
+ etch_join(thread[i]);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main all threads accounted for\n"); fflush(stdout);
+ #endif
+
+ for(i=0; i < TEST_WAIT_MULT_NUMTHREADS; i++) /* free thread objects */
+ etch_object_destroy(thread[i]);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_free_threadpool()
+ * test thread "pool" (ad hoc threads)
+ */
+static void test_free_threadpool(void)
+{
+ #define TEST_FREEPOOL_NUMTHREADS 4
+ etch_threadpool* threadpool = NULL;
+ void* runthread = NULL;
+ int waitms[TEST_FREEPOOL_NUMTHREADS], i, priori = 1;
+
+ for(i=0; i < TEST_FREEPOOL_NUMTHREADS; i++)
+ { /* initialize wait times for each thread */
+ const int newi = i & 1? priori - 1: priori + 2;
+ waitms[i] = ((priori = newi) * 1000);
+ }
+
+ threadpool = new_threadpool(ETCH_THREADPOOLTYPE_FREE, 0);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(threadpool);
+ threadpool->is_free_threads = TRUE;
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main creating %d pool threads ...\n", TEST_FREEPOOL_NUMTHREADS); fflush(stdout);
+ #endif
+
+ for(i=0; i < TEST_FREEPOOL_NUMTHREADS; i++) /* launch all threads */
+ {
+ waittestdata* thread_data = etch_malloc(sizeof(waittestdata),0);
+ thread_data->num = i; thread_data->flag = waitms[i];
+
+ runthread = threadpool->run (threadpool, wait_multiple_threadproc, thread_data);
+
+ CU_ASSERT_PTR_NOT_NULL(runthread);
+ }
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main all pool threads launched\n"); fflush(stdout);
+ printf("main destroying thread pool ...\n"); fflush(stdout);
+ #endif
+
+ etch_object_destroy(threadpool);
+
+ #if (IS_DEBUG_CONSOLE)
+ printf("main all threads accounted for\n"); fflush(stdout);
+ #endif
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int _tmain(int argc, _TCHAR* argv[])
+CU_pSuite test_etch_threadpool_suite()
+{
+ CU_pSuite ps = CU_add_suite("suite_threadpool", init_suite, clean_suite);
+
+ CU_add_test(ps, "test create thread", test_createthread);
+ CU_add_test(ps, "test etch mutex", test_mutex);
+ CU_add_test(ps, "test arraylist synchronization", test_synched_arraylist);
+ CU_add_test(ps, "test thread wait/start", test_wait);
+
+ CU_add_test(ps, "test wait on condition", test_wait_until);
+ CU_add_test(ps, "test wait on pre-existing condition", test_wait_until_preexisting);
+ CU_add_test(ps, "test wait on condition - negative", test_wait_until_negative_test);
+
+ CU_add_test(ps, "test wait all threads exit", test_wait_for_multiple);
+ CU_add_test(ps, "test free threadpool", test_free_threadpool);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/transport/test_transport.c b/binding-c/runtime/c/src/test/transport/test_transport.c
new file mode 100644
index 0000000..3706e13
--- /dev/null
+++ b/binding-c/runtime/c/src/test/transport/test_transport.c
@@ -0,0 +1,401 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_transport.c
+ * test delivery service etc
+ */
+#include "etch_runtime.h"
+#include "etch_transport.h"
+#include "etch_thread.h"
+#include "etch_default_value_factory.h"
+#include "etch_plain_mailbox_manager.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_map.h"
+#include "etch_log.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+
+#define IS_DEBUG_CONSOLE TRUE
+
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+
+static default_value_factory* new_bogus_valuefactory();
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * unit test support
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+#define FAKEID_TYPE_ADD 1
+#define FAKEID_TYPE_ADD_RESULT 2
+#define FAKEID_FIELD_X 3
+#define FAKEID_FIELD_Y 4
+#define FAKEID_FIELD_RESULT 5
+#define THISTEST_WHO_VALUE 0x5151
+
+static unsigned short CLASSID_MY_VF;
+static unsigned short CLASSID_MY_VF_VTAB;
+static unsigned short CLASSID_MY_VF_IMPL;
+static unsigned short CLASSID_MY_IMPL_TP;
+static unsigned short CLASSID_MY_IMPL_SM;
+
+static etch_resources* g_my_resources;
+static default_value_factory* g_my_vf;
+static vf_idname_map* g_my_typemap;
+static class_to_type_map* g_my_c2tmap;
+static etch_who* g_who;
+
+/**
+ * setup_this_test()
+ * set up an individual unit test
+ */
+static int setup_this_test()
+{
+ CLASSID_MY_VF = get_dynamic_classid();
+ CLASSID_MY_VF_VTAB = get_dynamic_classid();
+ CLASSID_MY_VF_IMPL = get_dynamic_classid();
+ CLASSID_MY_IMPL_TP = get_dynamic_classid();
+ CLASSID_MY_IMPL_SM = get_dynamic_classid();
+
+ g_my_vf = new_bogus_valuefactory();
+ set_etchobj_static_all(g_my_vf); /* so resources map will not destroy */
+
+ /* get resources map populated with transport resources such as thread pools */
+ g_my_resources = get_etch_transport_resources(NULL);
+ etch_resources_add(g_my_resources, ETCH_RESXKEY_MSGIZER_VALUFACT, (etch_object*) g_my_vf);
+
+ g_who = new_who(new_int32(THISTEST_WHO_VALUE));
+
+ #if(IS_DEBUG_CONSOLE)
+ printf("\n");
+ #endif
+ return 0;
+}
+
+
+/**
+ * teardown_this_test()
+ * tear down an individual unit test
+ */
+static void teardown_this_test()
+{
+ etch_object_destroy(g_my_resources);
+
+ if (g_my_vf)
+ { /* we clear the set_etchobj_static_all() on the g_my_vf value factory
+ * and as a result we can then destroy it */
+ clear_etchobj_static_all(g_my_vf);
+ etch_object_destroy(g_my_vf);
+ }
+
+ etch_object_destroy(g_my_c2tmap);
+ etch_object_destroy(g_my_typemap);
+
+
+ etch_object_destroy(g_who);
+
+ g_my_resources = NULL;
+ g_my_vf = NULL;
+ g_who = NULL;
+
+ etchvf_free_builtins();
+}
+
+
+/* - - - - - - - - - -
+ * value factory
+ * - - - - - - - - - -
+ */
+
+/**
+ * my_valufactory_impl
+ * value factory instance data object
+ */
+typedef struct my_valufactory_impl
+{
+ etch_object object;
+
+ etch_type* mt_add;
+ etch_type* mt_add_result;
+ etch_field* mf_x;
+ etch_field* mf_y;
+ etch_field* mf_result;
+
+} my_valufactory_impl;
+
+
+/**
+ * destroy_my_valufactory_impl()
+ * destructor for inheriting value factory instance data
+ */
+static int destroy_my_valufactory_impl(void* data)
+{
+ my_valufactory_impl* impl = (my_valufactory_impl*)data;
+ if (NULL == impl) return -1;
+
+ if (!is_etchobj_static_content(impl))
+ {
+ destroy_static_type(impl->mt_add);
+ destroy_static_type(impl->mt_add_result);
+ destroy_static_field(impl->mf_x);
+ destroy_static_field(impl->mf_y);
+ destroy_static_field(impl->mf_result);
+ }
+
+ return destroy_objectex((etch_object*) impl);
+}
+
+
+/**
+ * new_my_valufactory_impl()
+ * constructor for inheriting value factory instance data
+ */
+static my_valufactory_impl* new_my_valufactory_impl()
+{
+ unsigned short class_id = CLASSID_MY_VF_IMPL? CLASSID_MY_VF_IMPL:
+ (CLASSID_MY_VF_IMPL = get_dynamic_classid());
+
+ my_valufactory_impl* impl = (my_valufactory_impl*) new_object
+ (sizeof(my_valufactory_impl), ETCHTYPEB_VALUEFACTIMP, class_id);
+
+ ((etch_object*)impl)->destroy = destroy_my_valufactory_impl;
+
+ impl->mt_add = new_static_type(L"add");
+ impl->mt_add_result = new_static_type(L"add_result");
+ impl->mf_x = new_static_field(L"x");
+ impl->mf_y = new_static_field(L"y");
+ impl->mf_result = new_static_field(L"xresult");
+
+ /* we replace generated ids with 1-byte IDs to make test data buffers easier to construct */
+ impl->mt_add->id = FAKEID_TYPE_ADD;
+ impl->mt_add_result->id = FAKEID_TYPE_ADD_RESULT;
+ impl->mf_x->id = FAKEID_FIELD_X;
+ impl->mf_y->id = FAKEID_FIELD_Y;
+ impl->mf_result->id = FAKEID_FIELD_RESULT;
+
+ etchtype_put_validator(impl->mt_add, clone_field(impl->mf_x),
+ (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(impl->mt_add, clone_field(impl->mf_y),
+ (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(impl->mt_add, clone_field(builtins._mf__message_id),
+ (etch_object*) etchvtor_int64_get(0));
+
+ etchtype_put_validator(impl->mt_add_result, clone_field(impl->mf_result),
+ (etch_object*) etchvtor_int32_get(0));
+ etchtype_put_validator(impl->mt_add_result, clone_field(builtins._mf__message_id),
+ (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(impl->mt_add_result, clone_field(builtins._mf__in_reply_to),
+ (etch_object*) etchvtor_int64_get(0));
+
+ return impl;
+}
+
+
+/**
+ * new_bogus_valuefactory()
+ * constructor for value factory version 2 inheriting from default_value_factory
+ */
+static default_value_factory* new_bogus_valuefactory()
+{
+ etchparentinfo* inheritlist = NULL;
+ my_valufactory_impl* impl = NULL;
+
+ /* instantiate the new value factory.
+ * this vf does NOT own its type maps since we supply them here.
+ * however if we wanted to abandon ownership, we could clear the
+ * vf.is_own_types and vf.is_own_class_to_type flags here.
+ */
+ g_my_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(g_my_typemap);
+ /* since we explicitly instantiate a type map, and since we explicitly destroy
+ * the test's custom types, we want the type maps destructor to not destroy
+ * the map content. overriding the map's content clear callback is one way
+ * to do this. */
+ g_my_typemap->freehook = etch_noop_clear_handler;
+ g_my_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+ CU_ASSERT_PTR_NOT_NULL(g_my_c2tmap);
+ defvf_initialize_static(g_my_typemap, g_my_c2tmap);
+ g_my_vf = new_default_value_factory(g_my_typemap, g_my_c2tmap);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(g_my_vf);
+
+ /* ensure parent type keys exist in the (one-based) inheritance list.
+ * parent class of our custom vf is default_value_factory.
+ * inheritance list is used by validators and object assignment logic.
+ */
+ inheritlist = get_vtab_inheritance_list((etch_object*)g_my_vf, 2, 1, CLASSID_MY_VF_VTAB);
+ inheritlist[1].o.obj_type = ETCHTYPEB_VALUEFACTORY;
+ inheritlist[1].c.class_id = CLASSID_VALUEFACTORY; /* parent class */
+ ((etch_object*)g_my_vf)->class_id = CLASSID_MY_VF; /* our class */
+
+ /* instantiate the custom vf's instance data and assign it to the vf.
+ * the impl comprises all data specific to the inheriting class, including
+ * data and methods if any. the default value factory destructor will call
+ * the destructor on the vf's impl object.
+ */
+ impl = new_my_valufactory_impl();
+ g_my_vf->impl = (etch_object*) impl;
+
+ ((struct i_value_factory*)((etch_object*)g_my_vf)->vtab)->add_type(g_my_vf, impl->mt_add);
+ ((struct i_value_factory*)((etch_object*)g_my_vf)->vtab)->add_type(g_my_vf, impl->mt_add_result);
+
+ return g_my_vf;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - -
+ * unit tests
+ * - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * test_setup()
+ */
+static void test_setup(void)
+{
+ setup_this_test();
+
+ do
+ {
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * test_tcpds_construction()
+ * test tcp delivery service constructor and destructor
+ */
+static void test_tcpds_construction(void)
+{
+ setup_this_test();
+
+ do
+ { etch_tcp_delivery_service* tcpds = NULL;
+ etch_tcp_connection* nullconnection = NULL;
+ /* h_url* url = new_url(L"http://www.cisco.com:9999/cuae:?Messagizer.format=binary"); */
+ /* messagizer format is supplied in resource map - see get_etch_transport_resources() */
+ etch_url* url = new_url(L"http://www.cisco.com:9999/cuae");
+ etch_server_factory* impl_factory = new_server_factory (NULL, NULL, NULL, NULL);
+ impl_factory->in_resx = g_my_resources;
+
+ tcpds = new_tcp_delivery_service(url, (etch_factory_params*) impl_factory, nullconnection);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(tcpds);
+
+ etch_object_destroy(tcpds);
+
+ etch_object_destroy(url);
+ etch_object_destroy(impl_factory);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+
+/**
+ * test_transport_construction()
+ * test delivery service constructor and destructor
+ */
+static void test_transport_construction(void)
+{
+ setup_this_test();
+
+ do
+ { etch_server_factory* impl_factory = new_server_factory (NULL, NULL, NULL, NULL);
+ etch_tcp_connection* nullconnection = NULL;
+ i_delivery_service* ds = NULL;
+ impl_factory->in_resx = g_my_resources;
+
+ ds = new_etch_transport(L"http://www.cisco.com:9999/cuae",
+ (etch_factory_params*) impl_factory, nullconnection);
+
+ CU_ASSERT_PTR_NOT_NULL_FATAL(ds);
+
+ etch_object_destroy(ds); /* destroys transport implementation via interface */
+ etch_object_destroy(impl_factory);
+
+ } while(0);
+
+ teardown_this_test();
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int wmain( int argc, wchar_t* argv[], wchar_t* envp[])
+CU_pSuite test_etch_transport_suite()
+{
+ CU_pSuite ps = CU_add_suite("transport test suite", init_suite, clean_suite);
+
+ CU_add_test(ps, "test test setup and teardown", test_setup);
+ CU_add_test(ps, "test tcp delivery service constructor", test_tcpds_construction);
+ CU_add_test(ps, "test transport constructor", test_transport_construction);
+
+ return ps;
+}
diff --git a/binding-c/runtime/c/src/test/transport/test_url.c b/binding-c/runtime/c/src/test/transport/test_url.c
new file mode 100644
index 0000000..2b4d9ef
--- /dev/null
+++ b/binding-c/runtime/c/src/test/transport/test_url.c
@@ -0,0 +1,495 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version
+ * 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * test_url.c -- test etch_url
+ */
+#include "etch_runtime.h"
+#include "etch_url.h"
+#include "etch_map.h"
+#include "etch_thread.h"
+#include "etch_objecttypes.h"
+
+#include <stdio.h>
+#include "CUnit.h"
+#include <wchar.h>
+
+#define IS_DEBUG_CONSOLE FALSE
+
+// extern types
+extern apr_pool_t* g_etch_main_pool;
+
+/* - - - - - - - - - - - - - -
+ * unit test infrastructure
+ * - - - - - - - - - - - - - -
+ */
+
+static int init_suite(void)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = etch_runtime_initialize(NULL);
+ if(etch_status != NULL) {
+ // error
+ }
+ return 0;
+}
+
+static int clean_suite(void)
+{
+ etch_runtime_shutdown();
+ return 0;
+}
+
+/* - - - - - - - - - - - - - -
+ * tests
+ * - - - - - - - - - - - - - -
+ */
+
+/*
+ * test_parse_1
+ */
+static void test_parse_1(void)
+{
+ int result = 0;
+ etch_url* url = NULL;
+ wchar_t* RAWURL = L"www.cisco.com";
+
+ url = new_url(RAWURL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(url);
+
+ CU_ASSERT_NOT_EQUAL(url->bytecount,0);
+ CU_ASSERT_NOT_EQUAL(url->charcount,0);
+
+ result = wcscmp(url->raw, RAWURL);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->scheme, ETCH_URL_DEFAULT_SCHEME);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->host, RAWURL);
+ CU_ASSERT_EQUAL(result,0);
+
+ etch_object_destroy(url);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_parse_2
+ */
+static void test_parse_2(void)
+{
+ int result = 0;
+ etch_url* url = NULL;
+ wchar_t* RAWURL = L"http://www.cisco.com/cuae";
+
+ url = new_url(RAWURL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(url);
+
+ CU_ASSERT_NOT_EQUAL(url->bytecount,0);
+ CU_ASSERT_NOT_EQUAL(url->charcount,0);
+
+ result = wcscmp(url->raw, RAWURL);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->scheme, L"http");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->host, L"www.cisco.com");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->uri, L"cuae");
+ CU_ASSERT_EQUAL(result,0);
+
+ etch_object_destroy(url);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_parse_3
+ */
+static void test_parse_3(void)
+{
+ int result = 0;
+ etch_url* url = NULL;
+ wchar_t* RAWURL = L"http://www.cisco.com:8080/cuae?param";
+
+ url = new_url(RAWURL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(url);
+
+ CU_ASSERT_NOT_EQUAL(url->bytecount,0);
+ CU_ASSERT_NOT_EQUAL(url->charcount,0);
+
+ result = wcscmp(url->raw, RAWURL);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->scheme, L"http");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->host, L"www.cisco.com");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = url->port == 8080;
+ CU_ASSERT_EQUAL(result,TRUE);
+
+ result = wcscmp(url->uri, L"cuae");
+ CU_ASSERT_EQUAL(result,0);
+
+ etch_object_destroy(url);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_parse_4
+ */
+static void test_parse_4(void)
+{
+ int result = 0;
+ etch_url* url = NULL;
+ wchar_t* RAWURL = L"http://administrator:metreos@www.cisco.com:8080/cuae?param";
+
+ url = new_url(RAWURL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(url);
+
+ CU_ASSERT_NOT_EQUAL(url->bytecount,0);
+ CU_ASSERT_NOT_EQUAL(url->charcount,0);
+
+ result = wcscmp(url->raw, RAWURL);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->scheme, L"http");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->user, L"administrator");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->password, L"metreos");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->host, L"www.cisco.com");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = url->port == 8080;
+ CU_ASSERT_EQUAL(result,TRUE);
+
+ result = wcscmp(url->uri, L"cuae");
+ CU_ASSERT_EQUAL(result,0);
+
+ etch_object_destroy(url);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_parse_5
+ * since colon is both scheme and username:password delimiter
+ * ensure we can omit scheme and include password
+ */
+static void test_parse_5(void)
+{
+ int result = 0;
+ etch_url* url = NULL;
+ wchar_t* RAWURL = L"administrator:metreos@www.cisco.com:8080/cuae?param";
+
+ url = new_url(RAWURL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(url);
+
+ CU_ASSERT_NOT_EQUAL(url->bytecount,0);
+ CU_ASSERT_NOT_EQUAL(url->charcount,0);
+
+ result = wcscmp(url->raw, RAWURL);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->scheme, L"http");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->user, L"administrator");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->password, L"metreos");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->host, L"www.cisco.com");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = url->port == 8080;
+ CU_ASSERT_EQUAL(result,TRUE);
+
+ result = wcscmp(url->uri, L"cuae");
+ CU_ASSERT_EQUAL(result,0);
+
+ etch_object_destroy(url);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_parse_6
+ */
+static void test_parse_6(void)
+{
+ int result = 0;
+ etch_url* url = NULL;
+ wchar_t* RAWURL = L"tcp://administrator:metreos@localhost:10000/defUri;param1;param2?term1=true&term2=false#defFragment";
+
+ url = new_url(RAWURL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(url);
+
+ CU_ASSERT_NOT_EQUAL(url->bytecount,0);
+ CU_ASSERT_NOT_EQUAL(url->charcount,0);
+
+ result = wcscmp(url->raw, RAWURL);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->scheme, L"tcp");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->user, L"administrator");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->password, L"metreos");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->host, L"localhost");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = url->port == 10000;
+ CU_ASSERT_EQUAL(result,TRUE);
+
+ result = wcscmp(url->uri, L"defUri");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = etchurl_paramcount(url);
+ CU_ASSERT_EQUAL(result,2);
+
+ result = etchurl_termcount(url);
+ CU_ASSERT_EQUAL(result,2);
+
+ do
+ { etch_iterator* iterator = etchurl_get_params(url);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator);
+
+ while(iterator->has_next(iterator))
+ { int matches = 0;
+ etch_string* val = (etch_string*) iterator->current_value;
+ wchar_t* param = val? val->v.valw: NULL;
+ if (param && 0 == wcscmp(param, L"param1")) matches++;
+ if (param && 0 == wcscmp(param, L"param2")) matches++;
+ CU_ASSERT_EQUAL(matches,1);
+ iterator->next(iterator);
+ }
+ etch_object_destroy(iterator);
+ } while(0);
+
+ do
+ { etch_iterator iterator;
+ set_iterator(&iterator, url->terms, &url->terms->iterable);
+
+ while(iterator.has_next(&iterator))
+ { int keymatches = 0, valmatches = 0;
+ wchar_t* key = (wchar_t*) iterator.current_key;
+ etch_string* valobj = (etch_string*) iterator.current_value;
+ wchar_t* val = valobj? valobj->v.valw: NULL;
+ if (key && 0 == wcscmp(key, L"term1")) keymatches++;
+ if (key && 0 == wcscmp(key, L"term2")) keymatches++;
+ CU_ASSERT_EQUAL(keymatches,1);
+ if (val && 0 == wcscmp(val, L"false")) valmatches++;
+ if (val && 0 == wcscmp(val, L"true")) valmatches++;
+ CU_ASSERT_EQUAL(valmatches,1);
+ iterator.next(&iterator);
+ }
+ } while(0);
+
+ etch_object_destroy(url);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/*
+ * test_parse_7
+ * test a url with duplicate term names
+ */
+static void test_parse_7(void)
+{
+ int result = 0;
+ etch_url* url = NULL;
+ wchar_t* RAWURL = L"tcp://administrator:metreos@localhost:10000/defUri;param1;param2?term1=true&term1=false";
+
+ url = new_url(RAWURL);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(url);
+
+ CU_ASSERT_NOT_EQUAL(url->bytecount,0);
+ CU_ASSERT_NOT_EQUAL(url->charcount,0);
+
+ result = wcscmp(url->raw, RAWURL);
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->scheme, L"tcp");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->user, L"administrator");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->password, L"metreos");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = wcscmp(url->host, L"localhost");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = url->port == 10000;
+ CU_ASSERT_EQUAL(result,TRUE);
+
+ result = wcscmp(url->uri, L"defUri");
+ CU_ASSERT_EQUAL(result,0);
+
+ result = etchurl_paramcount(url);
+ CU_ASSERT_EQUAL(result,2);
+
+ /* since terms had same name, the two terms should now be collected into a set,
+ * therefore the count (at top level) should be 1. this is of course based
+ * on our omniscient knowledge of the url content */
+ result = etchurl_termcount(url);
+ CU_ASSERT_EQUAL(result,1);
+
+ do /* iterate all params */
+ { etch_iterator* iterator = etchurl_get_params(url);
+ CU_ASSERT_PTR_NOT_NULL_FATAL(iterator);
+
+ while(iterator->has_next(iterator))
+ { int matches = 0;
+ etch_string* val = (etch_string*) iterator->current_value;
+ wchar_t* param = val? val->v.valw: NULL;
+ if (param && 0 == wcscmp(param, L"param1")) matches++;
+ if (param && 0 == wcscmp(param, L"param2")) matches++;
+ CU_ASSERT_EQUAL(matches,1);
+
+ iterator->next(iterator);
+ }
+
+ etch_object_destroy(iterator);
+
+ } while(0);
+
+ do /* iterate all terms */
+ { etch_iterator iterator1;
+ set_iterator(&iterator1, url->terms, &url->terms->iterable);
+
+ /* this is hard coded base on our knowledge of the url content.
+ * there should be one term in the term map with the value "term1",
+ * and its value will be a set whose two members are etch_string
+ * objects having values of "true" and "false"
+ */
+ while(iterator1.has_next(&iterator1))
+ {
+ etch_iterator iterator2;
+ etch_set* setobj = (etch_set*)iterator1.current_value;
+ CU_ASSERT_EQUAL_FATAL(is_etch_set(setobj), TRUE);
+
+ result = etchmap_count(setobj);
+ CU_ASSERT_EQUAL(result, 2);
+
+ set_iterator(&iterator2, setobj, &setobj->iterable);
+
+ while(iterator2.has_next(&iterator2))
+ {
+ int matches = 0;
+ wchar_t* val = 0;
+ etch_string* setmember = iterator2.current_key;
+ CU_ASSERT_EQUAL_FATAL(is_etch_string(setmember), TRUE);
+ val = setmember->v.valw;
+ if (val && 0 == wcscmp(val, L"true")) matches++;
+ if (val && 0 == wcscmp(val, L"false")) matches++;
+ CU_ASSERT_EQUAL(matches,1);
+
+ iterator2.next(&iterator2);
+ }
+
+ iterator1.next(&iterator1);
+ }
+
+ } while(0);
+
+ etch_object_destroy(url);
+
+#ifdef ETCH_DEBUGALLOC
+ g_bytes_allocated = etch_showmem(0,IS_DEBUG_CONSOLE); /* verify all memory freed */
+ CU_ASSERT_EQUAL(g_bytes_allocated, 0);
+ // start fresh for next test
+ memtable_clear();
+#endif
+}
+
+
+/**
+ * main
+ */
+//int _tmain(int argc, _TCHAR* argv[])
+CU_pSuite test_etch_url_suite()
+{
+ CU_pSuite pSuite = CU_add_suite("suite_url", init_suite, clean_suite);
+
+ CU_add_test(pSuite, "test parse 1", test_parse_1);
+ CU_add_test(pSuite, "test parse 2", test_parse_2);
+ CU_add_test(pSuite, "test parse 3", test_parse_3);
+ CU_add_test(pSuite, "test parse 4", test_parse_4);
+ CU_add_test(pSuite, "test parse 5", test_parse_5);
+ CU_add_test(pSuite, "test parse 6", test_parse_6);
+ CU_add_test(pSuite, "test parse 7", test_parse_7);
+
+ return pSuite;
+}
+
diff --git a/build-support/etch.common.xml b/build-support/etch.common.xml
index fdaffc2..895f974 100644
--- a/build-support/etch.common.xml
+++ b/build-support/etch.common.xml
@@ -104,6 +104,8 @@
<property name="etch-java-compiler-src.zip" value="apache-etch-java-compiler-${Etch.longversion}-src.zip" />
<property name="etch-java-runtime.jar" value="apache-etch-java-runtime-${Etch.longversion}.jar" />
<property name="etch-java-runtime-src.zip" value="apache-etch-java-runtime-${Etch.longversion}-src.zip" />
+ <property name="etch-c-compiler.jar" value="apache-etch-c-compiler-${Etch.longversion}.jar" />
+ <property name="etch-c-compiler-src.zip" value="apache-etch-c-compiler-${Etch.longversion}-src.zip" />
<property name="etch-csharp-compiler.jar" value="apache-etch-csharp-compiler-${Etch.longversion}.jar" />
<property name="etch-csharp-compiler-src.zip" value="apache-etch-csharp-compiler-${Etch.longversion}-src.zip" />
<property name="etch-xml-compiler.jar" value="apache-etch-xml-compiler-${Etch.longversion}.jar" />
diff --git a/build.xml b/build.xml
index c7d6f6c..3189828 100755
--- a/build.xml
+++ b/build.xml
@@ -153,6 +153,7 @@
<build_component dir="binding-xml" />
<build_component dir="binding-java" />
<build_component dir="binding-csharp" />
+ <build_component dir="binding-c" />
<!-- Examples -->
<build_component dir="examples" />
diff --git a/examples/helloworld/README.txt b/examples/helloworld/README.txt
new file mode 100644
index 0000000..fb094cb
--- /dev/null
+++ b/examples/helloworld/README.txt
@@ -0,0 +1,23 @@
+The Helloworld example is a very very simple example to illustrate the interoperability
+of the c and the java binding.
+
+Of course you can also compile every ETCH IDL from the official Etch examples using the
+ETCH C IDL Compiler.
+
+This example comes already pre-generated for you, so you should have minimal effort getting it
+running. You can re-generate the code using the generate-c-and-java.bat in \helloworld\etch\
+
+To run the example, run the java server (an eclipse project is located in \helloworld\etch)
+and run the c client (a VS solution is provided in \helloworld\c). Feel free to play with IDL
+and sources to try out other Etch features. Currently the Helloworld c example implements
+only the client part.
+
+****************************************************
+ATTENTION: The C Binding needs libapr-1.dll and libapriconv-1.dll either in your PATH or
+in the working directory of the client executable!
+****************************************************
+
+Please note that the C Compiler does not overwrite impl-files (they might contain your Implementation).
+A side-effect is that you have to delete all impl headers by hand when adding e.g. a new function to the IDL.
+This will be changed in a later release.
+
diff --git a/examples/helloworld/c/helloworld.sln b/examples/helloworld/c/helloworld.sln
new file mode 100644
index 0000000..608e710
--- /dev/null
+++ b/examples/helloworld/c/helloworld.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "helloworld", "helloworld.vcproj", "{D660401E-5348-4F6B-B3A0-60FC2287788B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D660401E-5348-4F6B-B3A0-60FC2287788B}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D660401E-5348-4F6B-B3A0-60FC2287788B}.Debug|Win32.Build.0 = Debug|Win32
+ {D660401E-5348-4F6B-B3A0-60FC2287788B}.Release|Win32.ActiveCfg = Release|Win32
+ {D660401E-5348-4F6B-B3A0-60FC2287788B}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/examples/helloworld/c/helloworld.vcproj b/examples/helloworld/c/helloworld.vcproj
new file mode 100644
index 0000000..95d0294
--- /dev/null
+++ b/examples/helloworld/c/helloworld.vcproj
@@ -0,0 +1,300 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="helloworld"
+ ProjectGUID="{D660401E-5348-4F6B-B3A0-60FC2287788B}"
+ RootNamespace="helloworld"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""..\..\..\binding-c\runtime\c\src\extern\apr\apr\include";"..\..\..\binding-c\runtime\c\src\extern\apr\apr\include\arch\win32";"..\..\..\binding-c\runtime\c\src\extern\jenkhash";"..\..\..\binding-c\runtime\c\include""
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB;APR_DECLARE_STATIC"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="etch-c.lib jenkhash.lib libapr-1.lib libapriconv-1.lib"
+ AdditionalLibraryDirectories=""..\..\..\binding-c\runtime\c\src\extern\apr\apr\Debug";"..\..\..\binding-c\runtime\c\target\win32\Debug";"..\..\..\binding-c\runtime\c\src\extern\jenkhash\target\win32\Debug";"..\..\..\binding-c\runtime\c\src\extern\apr\apr-iconv\Debug""
+ GenerateDebugInformation="true"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ RuntimeLibrary="2"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ GenerateDebugInformation="true"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\helloworld_client.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_client_impl.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_client_implx.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_client_main.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_client_stub.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_helper.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_interface.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_remote.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_remote_client.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_remote_server.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_server.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_server_impl.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_server_implx.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_server_stub.c"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_valufact.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\helloworld_client.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_client_impl.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_client_main.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_client_stub.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_helper.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_interface.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_remote.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_remote_client.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_remote_server.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_server.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_server_impl.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_server_stub.h"
+ >
+ </File>
+ <File
+ RelativePath=".\helloworld_valufact.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/examples/helloworld/c/helloworld_client.c b/examples/helloworld/c/helloworld_client.c
new file mode 100644
index 0000000..617d9d5
--- /dev/null
+++ b/examples/helloworld/c/helloworld_client.c
@@ -0,0 +1,103 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#include "helloworld_client.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_url.h"
+
+unsigned short CLASSID_HELLOWORLD_CLIENT_BASE;
+
+
+int destroy_helloworld_client_base(void*);
+
+
+/* - - - - - - - - - - - - - -
+ * constructors
+ * - - - - - - - - - - - - - -
+ */
+/**
+ * new_helloworld_client_base()
+ * @param iservice service interface -- caller retains
+ */
+i_helloworld_client* new_helloworld_client_base(struct helloworld_client_impl* implobj)
+{
+ i_helloworld_client* ipc = (i_helloworld_client*) new_object (sizeof(i_helloworld_client),
+ ETCHTYPEB_EXECLIENTBASE, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_CLIENT_BASE));
+
+ ipc->thisx = implobj; /* helloworld_client_impl on client, null on server */
+ ((etch_object*)ipc)->destroy = destroy_helloworld_client_base;
+
+ ipc->ihelloworld = new_helloworld_service_interface();
+
+ ipc->say_hello = ipc->ihelloworld->say_hello;
+
+ ipc->user = ipc->ihelloworld->user;
+
+ return ipc;
+}
+
+/**
+ * destroy_helloworld_client_base()
+ * i_helloworld_client destructor.
+ */
+int destroy_helloworld_client_base (void* data)
+{
+ i_helloworld_client* ipc = (i_helloworld_client*)data;
+ if (NULL == ipc) return -1;
+
+ if (!is_etchobj_static_content(ipc))
+ {
+ if (ipc->thisx) /* thisx is null on server (i.e. this is a remote client) */
+ { /* destroy the helloworld_client_impl object */
+ ETCH_ASSERT(is_etch_client_impl((etch_object*)ipc->thisx));
+ //ETCHOBJ_DESTROY();
+ if(((etch_object*)ipc->thisx)){
+ etch_object_destroy(((etch_object*)ipc->thisx));
+ }
+ ipc->thisx = NULL;
+ }
+
+ //ETCHOBJ_DESTROY(ipc->ihelloworld);
+ if(ipc->ihelloworld){
+ etch_object_destroy(ipc->ihelloworld);
+ }
+ ipc->ihelloworld = NULL;
+
+ etch_free(ipc->iobjsession);
+ }
+
+ return destroy_objectex((etch_object*)ipc);
+}
+
+/* - - - - - - - - - - - - - -
+ * client base methods
+ * - - - - - - - - - - - - - -
+ */
+
+/* nothing to do - service defines no client-directed items */
+
diff --git a/examples/helloworld/c/helloworld_client.h b/examples/helloworld/c/helloworld_client.h
new file mode 100644
index 0000000..fde42e6
--- /dev/null
+++ b/examples/helloworld/c/helloworld_client.h
@@ -0,0 +1,100 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * helloworld_client.h
+ * helloworld client interface.
+ * combines java bindings's helloworldServer and BasehelloworldServer
+ */
+
+#ifndef HELLOWORLD_CLIENT_H
+#define HELLOWORLD_CLIENT_H
+
+#include "helloworld_interface.h"
+#include "etch_sessionint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_HELLOWORLD_CLIENT_BASE;
+
+//typedef struct helloworld_client_impl helloworld_client_impl;
+
+/**
+ * i_helloworld_client
+ * helloworld client base interface
+ */
+typedef struct i_helloworld_client
+{
+ etch_object object;
+
+ struct helloworld_client_impl* thisx;
+ i_helloworld* ihelloworld;
+
+
+
+ /* - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - -
+ */
+ i_objsession* iobjsession;
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - -
+ * service virtuals
+ * - - - - - - - - - - -
+ */
+ helloworld_say_hello say_hello;
+
+ /* no client-directed items defined */
+
+ /* - - - - - - - - - - -
+ * service data
+ * - - - - - - - - - - -
+ */
+ helloworld_user* user;
+
+ /* - - - - - - - - - - -
+ * private instance data
+ * - - - - - - - - - - -
+ */
+ int server_id;
+
+} i_helloworld_client;
+
+//i_helloworld_client* new_helloworld_client_base(helloworld_client_impl* implobj);
+i_helloworld_client* new_helloworld_client_base();
+
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* HELLOWORLD_CLIENT_H */
diff --git a/examples/helloworld/c/helloworld_client_impl.c b/examples/helloworld/c/helloworld_client_impl.c
new file mode 100644
index 0000000..aebb53d
--- /dev/null
+++ b/examples/helloworld/c/helloworld_client_impl.c
@@ -0,0 +1,92 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#include "helloworld_client_impl.h"
+#include "etch_url.h"
+#include "etch_arrayval.h"
+#include "etch_binary_tdo.h"
+#include "etch_exception.h"
+#include "etch_general.h"
+#include "etch_log.h"
+
+#include <stdio.h>
+
+unsigned short CLASSID_HELLOWORLD_CLIENT_IMPL;
+
+
+char* HELLOWORLD_ETCHCIMP = "CIMP";
+
+/* generated signatures */
+int destroy_helloworld_client_implx(helloworld_client_impl*);
+helloworld_client_impl* init_helloworld_client_impl(struct helloworld_remote_server*, etch_object_destructor);
+
+
+/* - - - - - - - -
+ * instantiation
+ * - - - - - - - -
+ */
+
+/**
+ * new_helloworld_client_impl()
+ * helloworld_client_impl constructor.
+ * add your custom initialization and virtual method overrides here.
+ */
+helloworld_client_impl* new_helloworld_client_impl(struct helloworld_remote_server* server)
+{
+ helloworld_client_impl* pclient /* allocate object and assign default virtuals */
+ = init_helloworld_client_impl(server, destroy_helloworld_client_implx);
+ /* add virtual method overrides, if any, here */
+ //pclient->xxx = implementation
+
+ return pclient;
+}
+
+
+/**
+ * destroy_helloworld_client_implx()
+ * destructor for any user allocated memory.
+ * this code is invoked by the private perf_client_impl destructor,
+ * via perf_client.destroyex(). add code here to destroy any memory you
+ * may have allocated for your custom perf_client implementation.
+ */
+int destroy_helloworld_client_implx(helloworld_client_impl* thisx)
+{
+ /* * * add custom destruction here * * */
+ //etch_free(thisx->exampleobj);
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - -
+ * session interface method overrides
+ * - - - - - - - - - - - - - - - - - - -
+ */
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * implementations of helloworld_client messages from server, if any
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
diff --git a/examples/helloworld/c/helloworld_client_impl.h b/examples/helloworld/c/helloworld_client_impl.h
new file mode 100644
index 0000000..7b01472
--- /dev/null
+++ b/examples/helloworld/c/helloworld_client_impl.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#ifndef HELLOWORLD_CLIENT_IMPL_H
+#define HELLOWORLD_CLIENT_IMPL_H
+
+#include "helloworld_client.h"
+#include "etch_transport.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_HELLOWORLD_CLIENT_IMPL;
+
+//typedef struct helloworld_remote_server helloworld_remote_server;
+
+/**
+ * helloworld_client_impl
+ * your custom implementation of helloworld_client. add methods here
+ * to provide implementations of messages from the client, if any.
+ */
+typedef struct helloworld_client_impl
+{
+ etch_object object;
+
+ i_helloworld_client* helloworld_client_base; /* owned */
+ i_helloworld* ihelloworld; /* not owned */
+ struct helloworld_remote_server* server; /* not owned */
+
+ int (*destroyex) (void*); /* user memory destructor */
+
+ /* - - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - - -
+ */
+ i_objsession* iobjsession; /* owned by base */
+ /* note that iobjsession->thisx is set to this helloworld_client_impl* */
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - - -
+ * base service virtuals
+ * - - - - - - - - - - - -
+ */
+ helloworld_say_hello say_hello;
+
+ void* context;
+
+
+} helloworld_client_impl;
+
+/* constructor */
+helloworld_client_impl* new_helloworld_client_impl (struct helloworld_remote_server*);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* HELLOWORLD_CLIENT_IMPL_H */
diff --git a/examples/helloworld/c/helloworld_client_implx.c b/examples/helloworld/c/helloworld_client_implx.c
new file mode 100644
index 0000000..ce73802
--- /dev/null
+++ b/examples/helloworld/c/helloworld_client_implx.c
@@ -0,0 +1,99 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * helloworld_client_implx.c
+ * $helper.getImplName functions which would ordinarily not be subject to edit.
+ */
+
+#include "helloworld_client_impl.h"
+#include "helloworld_remote_server.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_url.h"
+
+int destroy_helloworld_client_impl(void*);
+
+/* - - - - - - - - - - - - - - - - - - - - - - - -
+ *helloworld_client_impl private construction / destruction
+ * - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * init_helloworld_client_impl()
+ * called by helloworld_client_impl constructor to instantiate server implementation
+ * object and initialize with default virtuals.
+ * @param client the remote client, not owned.
+ * @param usermem_dtor destructor for any custom memory allocations.
+ */
+helloworld_client_impl* init_helloworld_client_impl(helloworld_remote_server* server,
+ etch_object_destructor usermem_dtor)
+{
+ helloworld_client_impl* pclient = (helloworld_client_impl*) new_object (sizeof(helloworld_client_impl),
+ ETCHTYPEB_EXECLIENTIMPL, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_CLIENT_IMPL));
+
+ pclient->server = server; /* not owned */
+ ((etch_object*)pclient)->destroy = destroy_helloworld_client_impl; /* private destructor */
+ pclient->destroyex = usermem_dtor; /* user memory destructor */
+
+ /* instantiate base and copy virtuals, if any, to this object */
+ pclient->helloworld_client_base = new_helloworld_client_base(pclient); /* owned */
+
+ pclient->ihelloworld = pclient->helloworld_client_base->ihelloworld;
+
+
+
+
+ pclient->iobjsession = server->server_base->iobjsession;
+ pclient->iobjsession->thisx = pclient; /* set implementor reference */
+ pclient->_session_control = pclient->helloworld_client_base->_session_control;
+ pclient->_session_notify = pclient->helloworld_client_base->_session_notify;
+ pclient->_session_query = pclient->helloworld_client_base->_session_query;
+
+ return pclient;
+}
+
+/**
+ * destroy_perf_server_impl()
+ * perf_server_impl private destructor.
+ * calls back to user destructor to effect cleanup of any perf_server_impl
+ * memory which may have been allocated in custom code added by user.
+ */
+int destroy_helloworld_client_impl (void* data)
+{
+ helloworld_client_impl* thisx = (helloworld_client_impl*)data;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ if(thisx->destroyex)
+ { /* call back to user memory destructor */
+ thisx->destroyex(thisx);
+ }
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
diff --git a/examples/helloworld/c/helloworld_client_main.c b/examples/helloworld/c/helloworld_client_main.c
new file mode 100644
index 0000000..b943caa
--- /dev/null
+++ b/examples/helloworld/c/helloworld_client_main.c
@@ -0,0 +1,112 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+/*
+ * helloworld_client_main.c
+ */
+
+#include "helloworld_client_main.h"
+#include "etch_objecttypes.h"
+#include "etch_runtime.h"
+#include "etch_arrayval.h"
+#include "etch_nativearray.h"
+#include "etch_binary_tdo.h"
+#include "etch_general.h"
+
+
+/**
+ * new_helloworld_client().
+ * callback constructor for client implementation object.
+ * this callback address is passed to start_helloworld_client() in [main].
+ * @param server the remote server.
+ * @remarks this callback must be supplied, i.e. its functionality cannot be
+ * defaulted, since the client implementation constructor new_helloworld_client_impl()
+ * is not known to start_helloworld_client().
+ */
+static i_helloworld_client* helloworld_client_create(void* factory_thisx, helloworld_remote_server* server)
+{
+ helloworld_client_impl* client = new_helloworld_client_impl(server);
+ return client? client->helloworld_client_base:NULL;
+}
+
+/**
+ * main()
+ */
+int main(int argc, char* argv[])
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ helloworld_remote_server* remote = NULL;
+ int result = -1, is_waitkey = TRUE, waitupms = 4000;
+ helloworld_user* user = new_helloworld_user();
+ etch_string* answer = NULL;
+
+ wchar_t* uri = L"tcp://127.0.0.1:4004";
+
+ etch_config_t* config = NULL;
+ etch_config_create(&config);
+ // set properties or read file
+
+ etch_status = etch_runtime_initialize(config);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ return 1;
+ }
+
+ etch_status = helloworld_helper_remote_server_create(&remote, uri, NULL, helloworld_client_create);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+
+ etch_status = helloworld_helper_remote_server_start_wait(remote, waitupms);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+
+ //add your implementation here
+
+ user->name = new_stringw(L"Testuser");
+ answer = remote->say_hello(remote,user);
+ if(answer && ! is_etch_exception(answer)){
+ wprintf(L"Server said: %s\n", answer->v.valw);
+ }else{
+ wprintf(L"Remote call failed, is the server running?\n");
+ }
+
+ // wait until key press
+ waitkey();
+
+ etch_status = helloworld_helper_remote_server_stop_wait(remote, waitupms);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+
+ etch_status = helloworld_helper_remote_server_destroy(remote);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+ etch_config_destroy(config);
+ return 0;
+}
+
diff --git a/examples/helloworld/c/helloworld_client_main.h b/examples/helloworld/c/helloworld_client_main.h
new file mode 100644
index 0000000..b8b4244
--- /dev/null
+++ b/examples/helloworld/c/helloworld_client_main.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+/*
+ * helloworld_client_main.h
+ * client exe main() private header
+ */
+
+#ifndef HELLOWORLD_CLIENT_MAIN_H
+#define HELLOWORLD_CLIENT_MAIN_H
+
+#include "etch_runtime.h"
+#include "helloworld_helper.h"
+#include "helloworld_client_impl.h"
+#include "helloworld_remote_server.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * helloworld_listener_start(pplistener, uri, waitupms)
+ * Start the listener at the given uri, waiting waitupms microseconds for listener startup.
+ * The created listener is saved at address pointed to by pplistener.
+ */
+extern etch_status_t helloworld_listener_start(i_sessionlistener** pplistener, wchar_t* uri, int waitupms);
+
+/*
+ * helloworld_listener_stop(plistener, waitupms)
+ * Stop the listener given by plistener, waiting waitupms microseconds to stop.
+ */
+extern etch_status_t helloworld_listener_stop(i_sessionlistener* plistener, int waitupms);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* HELLOWORLD_CLIENT_MAIN.H */
diff --git a/examples/helloworld/c/helloworld_client_stub.c b/examples/helloworld/c/helloworld_client_stub.c
new file mode 100644
index 0000000..61db16b
--- /dev/null
+++ b/examples/helloworld/c/helloworld_client_stub.c
@@ -0,0 +1,146 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * helloworld_client_stub.c
+ */
+
+#include "helloworld_client.h"
+#include "helloworld_client_stub.h"
+#include "helloworld_valufact.h"
+
+#include "etch_url.h"
+#include "etch_objecttypes.h"
+#include "etch_svcobj_masks.h"
+#include "etch_general.h"
+
+
+unsigned short CLASSID_HELLOWORLD_CLIENT_STUB;
+
+
+int destroy_helloworld_client_stub(void*);
+
+
+/* - - - - - - - - - - -
+ * stub helper methods
+ * - - - - - - - - - - -
+ */
+
+/**
+ * helloworld_client_stub_run_nothing_
+ */
+int helloworld_client_stub_run_nothing_ (etch_stub* stub, i_delivery_service* dsvc,
+ void* obj, etch_who* whofrom, etch_message* msg)
+{
+ i_helloworld_client* client = (i_helloworld_client*)obj;
+ helloworld_valufact_impl* pvfi = NULL;
+ helloworld_valufact* pvf = NULL;
+ struct helloworld_client_impl* impl = NULL;
+
+ /* objects specific to service.nothing_() */
+ etch_field* key_foo = NULL;
+ etch_int64* val_foo = NULL;
+ etch_field* key_bar = NULL;
+ etch_string* val_bar = NULL;
+ etch_int32* resultobj = NULL;
+
+ etchstub_validate_args (stub, dsvc, msg, client, &pvf, (void**)&pvfi, (void**)&impl);
+
+ key_foo = NULL;
+ key_bar = NULL;
+ ETCH_ASSERT(key_foo && key_bar);
+
+ /* nullarg asserts are initial tests only: server impl
+ * will generate exceptions on nullargs in the real world */
+ val_foo = NULL; /* = (etch_int64*) message_get (msg, key_foo); */
+ val_bar = NULL; /* = (etch_string*) message_get (msg, key_bar); */
+ ETCH_ASSERT(val_foo && val_bar);
+
+ /* execute the service method */
+ resultobj = NULL; /* server->nothing_ (impl, val_foo, val_bar); */
+ ETCH_ASSERT(resultobj);
+
+ /* transmit reply back to sender */
+ return etchstub_send_reply (stub, dsvc, whofrom, msg, (void*) resultobj, TRUE);
+}
+
+/* - - - - - - - - -
+ * constructors
+ * - - - - - - - - -
+ */
+
+/**
+ * new_helloworld_client_stub.
+ * called from $helper.getRemoteName($intf, $helper.getRemoteDirection($mc))* perfhelper.new_remote_SERVER().
+ * @param p client parameter bundle
+ */
+helloworld_client_stub* new_helloworld_client_stub (etch_client_factory* p)
+{
+ helloworld_client_stub* mystub = NULL;
+ i_delivery_service* ids = p->dsvc;
+ etch_threadpool *qp = p->qpool, *fp = p->fpool;
+
+ i_helloworld_client* client = p->iclient;
+ ETCH_ASSERT(is_etch_ideliverysvc(ids));
+ ETCH_ASSERT(is_etch_client_base(client));
+
+ mystub = new_clientstub_init (client, sizeof(helloworld_client_stub),
+ destroy_helloworld_client_stub, ids, qp, fp, p);
+
+ ((etch_object*)mystub)->class_id = get_dynamic_classid_unique(&CLASSID_HELLOWORLD_CLIENT_STUB);
+ mystub->server_id = p->server_id;
+
+ /* initialize custom methods and data here */
+
+ /* set stub helper methods */
+
+ return mystub;
+}
+
+/**
+ * is_helloworld_client_stub()
+ */
+int is_helloworld_client_stub(void* obj)
+{
+ return obj && ((etch_object*)obj)->class_id == CLASSID_HELLOWORLD_CLIENT_STUB;
+}
+
+/**
+ * destroy_def_helloworld_client_stub()
+ * helloworld_client_stub user-allocated memory destructor.
+ * called back from private object destructor destroy_stub_object().
+ * if you explicitly allocate memory in the client stub object, destroy it here.
+ */
+int destroy_helloworld_client_stub(void* data)
+{
+ /*
+ helloworld_client_stub* mystub = (helloworld_client_stub*)data;
+ free custom memory allocations here */
+ //etch_free(mystub->my_example_obj); /* free example alloc */
+
+ return 0;
+}
diff --git a/examples/helloworld/c/helloworld_client_stub.h b/examples/helloworld/c/helloworld_client_stub.h
new file mode 100644
index 0000000..583dd88
--- /dev/null
+++ b/examples/helloworld/c/helloworld_client_stub.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#ifndef HELLOWORLD_CLIENT_STUB_H
+#define HELLOWORLD_CLIENT_STUB_H
+
+#include "etch_stub.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_HELLOWORLD_CLIENT_STUB;
+
+/**
+ * helloworld_client_stub
+ */
+typedef struct helloworld_client_stub
+{
+ etch_object object;
+
+ etch_stub* stub_base;
+
+ int server_id;
+ int (*destroyex) (void*); /* user memory destructor */
+
+ /* - - - - - - - - - - - - - - - - -
+ * client_directed service virtuals
+ * - - - - - - - - - - - - - - - - -
+ */
+
+ /* no client-directed items defined */
+
+ /* - - - - - - - - - - - - - - - - -
+ * service-specific allocations
+ * - - - - - - - - - - - - - - - - -
+ */
+ // add here
+
+} helloworld_client_stub;
+
+helloworld_client_stub* new_helloworld_client_stub (etch_client_factory*);
+int is_helloworld_client_stub(void* obj);
+int destroy_helloworld_client_stub (void*);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* PERF_SERVER_STUB_H */
+
diff --git a/examples/helloworld/c/helloworld_helper.c b/examples/helloworld/c/helloworld_helper.c
new file mode 100644
index 0000000..c488048
--- /dev/null
+++ b/examples/helloworld/c/helloworld_helper.c
@@ -0,0 +1,337 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+
+/*
+ * helloworld_helper.c
+ * transport helper for helloworld service
+ */
+#include "helloworld_interface.h"
+
+#include "helloworld_server.h"
+#include "helloworld_server_stub.h"
+#include "helloworld_remote_client.h"
+#include "helloworld_client_stub.h"
+#include "helloworld_remote_server.h"
+
+#include "helloworld_helper.h"
+#include "helloworld_valufact.h"
+#include "etch_svcobj_masks.h"
+#include "etch_objecttypes.h"
+#include "etch_url.h"
+#include "etch_log.h"
+
+static const char* LOG_CATEGORY = "helloworld_helper";
+
+static int helloworld_helper_resources_init(void* data)
+{
+ etch_server_factory* factory = (etch_server_factory*)data;
+ int result = 0;
+ ETCH_ASSERT((factory != NULL) && (factory->in_valufact == NULL));
+ ETCH_ASSERT (factory->in_resx && is_etch_hashtable(factory->in_resx));
+
+ // TODO use new semantic helloworld_valuefactory_create
+ factory->in_valufact = (etch_value_factory*)new_helloworld_valufact();
+ ETCH_ASSERT(factory->in_valufact);
+ if(factory->in_valufact == NULL) {
+ return -1;
+ }
+ result = etch_resources_add(factory->in_resx, ETCH_RESXKEY_MSGIZER_VALUFACT, (etch_object*)factory->in_valufact);
+
+ ETCH_ASSERT(0 == result);
+ return result;
+}
+
+etch_status_t helloworld_helper_remote_server_create(helloworld_remote_server** remote_server, wchar_t* uri, void* factory_thisx, main_client_create_func client_create)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ etch_client_factory* factory = NULL;
+ helloworld_remote_server* newremote_server = NULL;
+ i_helloworld_client* client = NULL;
+ helloworld_client_stub* client_stub = NULL;
+ helloworld_valufact* vf = NULL;
+
+ if(remote_server == NULL || client_create == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "creating helloworld client ...\n");
+ factory = new_client_factory (NULL, NULL, client_create);
+ ETCH_ASSERT(factory != NULL);
+
+ factory->thisx = factory_thisx;
+
+ vf = new_helloworld_valufact();
+ ETCH_ASSERT(vf != NULL);
+
+ factory->in_valufact = (etch_value_factory*)vf;
+ factory->in_resx = etch_transport_resources_init(factory->in_resx);
+ ETCH_ASSERT(factory->in_resx != NULL);
+ etch_resources_add (factory->in_resx, ETCH_RESXKEY_MSGIZER_VALUFACT, (etch_object*)vf);
+
+ // instantiate a delivery service
+ factory->dsvc = new_etch_transport(uri, (etch_factory_params*)factory, NULL);
+ ETCH_ASSERT(is_etch_ideliverysvc(factory->dsvc));
+
+ // instantiate the remote server
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "instantiating remote server ...\n");
+ newremote_server = new_helloworld_remote_server(NULL, factory->dsvc, (etch_value_factory*)vf);
+ ETCH_ASSERT(is_etch_remote_server(newremote_server));
+
+ factory->server_id = newremote_server->server_base->server_id;
+ factory->server = newremote_server;
+ newremote_server->client_factory = factory;
+
+ /* here we call back to the client constructor in [main]. the purpose of the
+ * callback is to isolate the editable xxxx_client_impl constructor from the
+ * private constructor pieces. the callback instantiates a client implenentation
+ * and returns an interface to it.
+ */
+ if(factory->new_client != NULL) {
+ client = factory->new_client(factory, newremote_server);
+ ETCH_ASSERT(is_etch_client_base(client));
+ factory->iclient = client;
+ }
+
+ // get thread pools
+ factory->fpool = (etch_threadpool*)etch_resources_get(factory->in_resx, ETCH_RESXKEY_POOLTYPE_FREE);
+ ETCH_ASSERT(factory->fpool);
+ factory->qpool = (etch_threadpool*)etch_resources_get(factory->in_resx, ETCH_RESXKEY_POOLTYPE_QUEUED);
+ ETCH_ASSERT(factory->qpool);
+
+ // construct client stub
+ client_stub = new_helloworld_client_stub(factory);
+ ETCH_ASSERT(is_etch_client_stub(client_stub));
+ factory->stub = client_stub;
+
+ *remote_server = newremote_server;
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG,"remote server instantiated\n");
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "helloworld client created\n");
+
+ return rv;
+}
+
+etch_status_t helloworld_helper_remote_server_start_wait(helloworld_remote_server* remote_server, const int waitms)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ helloworld_remote* remote = NULL;
+ int result = 0;
+
+ if(remote_server == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ remote = remote_server->remote_base;
+ ETCH_ASSERT(remote != NULL);
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "starting helloworld client ...\n");
+ result = remote->start_waitup(remote, waitms);
+ if(result != 0) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not start helloworld client\n");
+ rv = ETCH_ERROR;
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "helloworld client started\n");
+ rv = ETCH_SUCCESS;
+ }
+
+ return rv;
+}
+
+etch_status_t helloworld_helper_remote_server_stop_wait(helloworld_remote_server* remote_server, const int waitms)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ helloworld_remote* remote = NULL;
+ int result = 0;
+
+ if(remote_server == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ remote = remote_server->remote_base;
+ ETCH_ASSERT(remote != NULL);
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "stopping helloworld client ...\n");
+ result = remote->stop_waitdown(remote, waitms);
+ if(result != 0) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not stop helloworld client\n");
+ rv = ETCH_ERROR;
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "helloworld client stopped\n");
+ rv = ETCH_SUCCESS;
+ }
+
+ return rv;
+}
+
+etch_status_t helloworld_helper_remote_server_destroy(helloworld_remote_server* remote_server)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+
+ if(remote_server == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "destroying remote server ...\n");
+ etch_object_destroy(remote_server);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "remote server destroyed\n");
+
+ return rv;
+}
+
+static void* helloworld_helper_listener_create_func(void* factoryData, void* sessionData)
+{
+ etch_server_factory* factory = (etch_server_factory*)factoryData;
+ etch_session* session = (etch_session*)sessionData;
+ i_helloworld_server* iserver;
+ helloworld_server_stub* stub;
+ helloworld_remote_client* client;
+ ETCH_ASSERT(factory && factory->helper_new_listener && factory->main_new_server);
+ ETCH_ASSERT(factory->in_resx && factory->in_valufact); // TODO assert delivery service
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "instantiating accepted client listener ...\n");
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "creating remote client...\n");
+ client = new_helloworld_remote_client(NULL, session, factory->in_valufact);
+ client->session_id = session->session_id;
+ session->client = client;
+
+ /* here we CALL BACK to the constructor in [main], the purpose of the callback
+ * being to isolate the editable constructor from the private constructor.
+ * the callback instantiates a client's server implementation and returns
+ * an interface to it.
+ */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "creating server implementation ...\n");
+ iserver = factory->main_new_server(factory, session);
+ iserver->session_id = session->session_id;
+ session->server = iserver;
+
+ /* note that the main listener will use p->mainpool as a thread manager, not these */
+ factory->qpool = (etch_threadpool*)etch_resources_get(factory->in_resx, ETCH_RESXKEY_POOLTYPE_QUEUED);
+ factory->fpool = (etch_threadpool*)etch_resources_get(factory->in_resx, ETCH_RESXKEY_POOLTYPE_FREE);
+
+ /* eventually new_helloworld_server_stub() gets to stub_base constructor, which sets
+ * the delivery service's session to this, the server stub. so, in the java binding,
+ * the server stub is referenced as delivery service.session. we should perhaps also
+ * store the stub opaquely in both the client and listener objects.
+ */
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "creating server stub ...\n");
+ stub = new_helloworld_server_stub(factory, session);
+ stub->session_id = session->session_id;
+ session->server_stub = stub;
+
+ if (iserver && stub)
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "accepted client listener instantiated\n");
+ else
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not instantiate accepted client listener\n");
+
+ return stub;
+}
+
+etch_status_t helloworld_helper_listener_create(i_sessionlistener** listener, wchar_t* uri, void* factory_thisx, main_server_create_func server_create)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ i_sessionlistener* newlistener = NULL;
+
+ if(listener == NULL || server_create == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "instantiating main listener ...\n");
+
+ newlistener = new_etch_listener(uri, NULL, factory_thisx, helloworld_helper_listener_create_func, server_create, helloworld_helper_resources_init);
+ if(newlistener == NULL) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not instantiate main listener\n");
+ rv = ETCH_ERROR;
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "main listener instantiated\n");
+ *listener = newlistener;
+ rv = ETCH_SUCCESS;
+ }
+
+ return rv;
+}
+
+etch_status_t helloworld_helper_listener_start_wait(i_sessionlistener* listener, const int waitms)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ int result = 0;
+
+ if(listener == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "starting main listener ...\n");
+
+ result = listener->transport_control(listener->thisx, new_etch_event(CLASSID_CONTROL_START_WAITUP, waitms), NULL);
+ if(result != 0) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not start main listener\n");
+ rv = ETCH_ERROR;
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "main listener started on thread %d\n", transport_thread_id(listener));
+ }
+
+ return rv;
+}
+
+etch_status_t helloworld_helper_listener_stop_wait(i_sessionlistener* listener, const int waitms)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+ int result = 0;
+
+ if(listener == NULL) {
+ return ETCH_EINVAL;
+ }
+
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "stopping main listener ...\n");
+
+ result = listener->transport_control(listener->thisx, new_etch_event(CLASSID_CONTROL_STOP_WAITDOWN, waitms), NULL);
+ if(result != 0) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not stop main listener\n");
+ rv = ETCH_ERROR;
+ } else {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_INFO, "main listener ended\n");
+
+ if (transport_session_count (listener) > 0) {
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "begin client sessions teardown\n");
+ result = transport_teardown_client_sessions(listener);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_DEBUG, "end client sessions teardown\n");
+ }
+ }
+
+ return rv;
+}
+
+etch_status_t helloworld_helper_listener_destroy(i_sessionlistener* listener)
+{
+ etch_status_t rv = ETCH_SUCCESS;
+
+ etch_object_destroy(listener);
+
+ return rv;
+}
+
+ //isSERVER
diff --git a/examples/helloworld/c/helloworld_helper.h b/examples/helloworld/c/helloworld_helper.h
new file mode 100644
index 0000000..cb0b604
--- /dev/null
+++ b/examples/helloworld/c/helloworld_helper.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+
+#ifndef HELLOWORLD_HELPER_H
+#define HELLOWORLD_HELPER_H
+
+#include "etch.h"
+#include "etch_errno.h"
+#include "etch_transport.h"
+#include "helloworld_remote_server.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+etch_status_t helloworld_helper_remote_server_create(helloworld_remote_server** remote_server, wchar_t* uri, void* factory_thisx, main_client_create_func client_create);
+etch_status_t helloworld_helper_remote_server_start_wait(helloworld_remote_server* remote_server, const int waitms);
+etch_status_t helloworld_helper_remote_server_stop_wait(helloworld_remote_server* remote_server, const int waitms);
+etch_status_t helloworld_helper_remote_server_destroy(helloworld_remote_server* remote_server);
+
+etch_status_t helloworld_helper_listener_create(i_sessionlistener** listener, wchar_t* uri, void* factory_thisx, main_server_create_func);
+etch_status_t helloworld_helper_listener_start_wait(i_sessionlistener* listener, const int waitms);
+etch_status_t helloworld_helper_listener_stop_wait(i_sessionlistener* listener, const int waitms);
+etch_status_t helloworld_helper_listener_destroy(i_sessionlistener* listener);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* HELLOWORLD_HELPER_H */
+
diff --git a/examples/helloworld/c/helloworld_interface.c b/examples/helloworld/c/helloworld_interface.c
new file mode 100644
index 0000000..a00bb8a
--- /dev/null
+++ b/examples/helloworld/c/helloworld_interface.c
@@ -0,0 +1,220 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#include "helloworld_interface.h"
+#include "etch_url.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_cache.h"
+
+
+
+unsigned short CLASSID_HELLOWORLD_SERVICE_INTERFACE;
+unsigned short CLASSID_HELLOWORLD_USER;
+unsigned short CLASSID_HELLOWORLD_USERUNKNOWNEXCEPTION;
+
+
+etch_string* helloworld_def_say_hello(void* thisx, helloworld_user* to_whom);
+
+int destroy_helloworld_service_interface (void*);
+
+
+/*
+ * destructors
+ */
+/**
+ * destroy_helloworld_user()
+ * user object destructor
+ */
+int destroy_helloworld_user(void* data)
+{
+ helloworld_user* this = (helloworld_user*)data;
+ if(!((etch_object*)this)->is_static && this->name)
+ etch_object_destroy(this->name);
+ destroy_object(this);
+ return 0;
+}
+/**
+ * destroy_helloworld_UserUnknownException()
+ * userunknownexception object destructor
+ */
+int destroy_helloworld_UserUnknownException(void* data)
+{
+ helloworld_UserUnknownException* this = (helloworld_UserUnknownException*)data;
+ if(!((etch_object*)this)->is_static)
+ etch_object_destroy(this->message);
+ if(!((etch_object*)this)->is_static && this->mes)
+ etch_object_destroy(this->mes);
+ destroy_object(this);
+ return 0;
+}
+
+/* - - - - - - - - - - - - - -
+ * constructors
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_helloworld_user()
+ * user object constructor.
+ */
+helloworld_user* new_helloworld_user()
+{
+ helloworld_user* user = (helloworld_user*) new_object(sizeof(helloworld_user),
+ ETCHTYPEB_USER, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USER));
+
+ ((etch_object*)user)->destroy = destroy_helloworld_user;
+
+ return user;
+}
+
+
+
+
+
+/**
+ * clone_helloworld_user()
+ * user object copy constructor.
+ */
+helloworld_user* clone_helloworld_user(helloworld_user* other)
+{
+ helloworld_user* user = (helloworld_user*) new_object(sizeof(helloworld_user),
+ ETCHTYPEB_USER, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USER));
+
+ user->id = other->id;
+ user->name = other->name;
+
+ ((etch_object*)user)->destroy = destroy_helloworld_user;
+
+ return user;
+}
+
+
+/**
+ * is_helloworld_user()
+ */
+int is_helloworld_user(void* x)
+{
+ return x && ((etch_object*)x)->class_id == CLASSID_HELLOWORLD_USER;
+}
+
+/**
+ * new_helloworld_UserUnknownException()
+ * userunknownexception object constructor.
+ */
+helloworld_UserUnknownException* new_helloworld_UserUnknownException()
+{
+
+ helloworld_UserUnknownException* userunknownexception = (helloworld_UserUnknownException*) new_object(sizeof(helloworld_UserUnknownException),
+ ETCHTYPEB_EXCEPTION, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USERUNKNOWNEXCEPTION));
+
+ userunknownexception->message = new_stringw(L"user generated exception, no default exception message.");
+ userunknownexception->errorcode = ETCH_ERROR;
+ userunknownexception->excptype = EXCPTYPE_USERDEFINED;
+
+
+ ((etch_object*)userunknownexception)->destroy = destroy_helloworld_UserUnknownException;
+
+ return userunknownexception;
+}
+
+
+
+
+
+/**
+ * clone_helloworld_UserUnknownException()
+ * userunknownexception object copy constructor.
+ */
+helloworld_UserUnknownException* clone_helloworld_UserUnknownException(helloworld_UserUnknownException* other)
+{
+ helloworld_UserUnknownException* userunknownexception = (helloworld_UserUnknownException*) new_object(sizeof(helloworld_UserUnknownException),
+ ETCHTYPEB_EXCEPTION, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USERUNKNOWNEXCEPTION));
+
+ userunknownexception->mes = other->mes;
+
+ ((etch_object*)userunknownexception)->destroy = destroy_helloworld_UserUnknownException;
+
+ return userunknownexception;
+}
+
+
+/**
+ * is_helloworld_UserUnknownException()
+ */
+int is_helloworld_UserUnknownException(void* x)
+{
+ return x && ((etch_object*)x)->class_id == CLASSID_HELLOWORLD_USERUNKNOWNEXCEPTION;
+}
+
+
+/**
+ * new_helloworld_service_interface
+ */
+i_helloworld* new_helloworld_service_interface ()
+{
+ i_helloworld* isvc = (i_helloworld*) new_object (sizeof(i_helloworld), ETCHTYPEB_SVCINTERFACE,
+ get_dynamic_classid_unique(&CLASSID_HELLOWORLD_SERVICE_INTERFACE));
+
+ ((etch_object*)isvc)->destroy = destroy_helloworld_service_interface;
+
+ isvc->say_hello = helloworld_def_say_hello;
+
+ isvc->user = new_helloworld_user();
+
+ return isvc;
+}
+
+/**
+ * destroy_helloworld_service_interface()
+ * i_helloworld destructor.
+ */
+int destroy_helloworld_service_interface (void* data)
+{
+ i_helloworld* isvc = (i_helloworld*)data;
+ if (NULL == isvc) return -1;
+
+ if (!is_etchobj_static_content(isvc))
+ {
+ etch_object_destroy(isvc->user);
+ }
+
+ return destroy_objectex((etch_object*)isvc);
+}
+
+
+/* - - - - - - - - - - - - - -
+ * service method stubs
+ * - - - - - - - - - - - - - -
+ */
+
+etch_string* helloworld_def_say_hello(void* thisx, helloworld_user* to_whom)
+{
+ etch_free(to_whom);
+ return NULL;
+}
+
diff --git a/examples/helloworld/c/helloworld_interface.h b/examples/helloworld/c/helloworld_interface.h
new file mode 100644
index 0000000..d4eb54c
--- /dev/null
+++ b/examples/helloworld/c/helloworld_interface.h
@@ -0,0 +1,125 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#ifndef HELLOWORLD_H
+#define HELLOWORLD_H
+
+#include "etch_object.h"
+#include "etch_mailbox.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_HELLOWORLD_SERVICE_INTERFACE;
+extern unsigned short CLASSID_HELLOWORLD_USER;
+extern unsigned short CLASSID_HELLOWORLD_USERUNKNOWNEXCEPTION;
+
+//typedef struct helloworld_user helloworld_user;
+
+
+
+
+
+/**
+ * helloworld_user
+ * helloworld service value object user
+ */
+typedef struct helloworld_user
+{
+ etch_object object;
+
+
+ int id;
+ etch_string* name;
+
+ } helloworld_user;
+
+helloworld_user* new_helloworld_user();
+helloworld_user* clone_helloworld_user(helloworld_user* other);
+int is_helloworld_user(void* obj);
+
+/**
+ * helloworld_UserUnknownException
+ * helloworld service value object userunknownexception
+ */
+typedef struct helloworld_UserUnknownException
+{
+ etch_object object;
+
+ etch_string* message;
+ uint32 errorcode;
+ excptype_t excptype;
+
+ etch_string* mes;
+
+ } helloworld_UserUnknownException;
+
+helloworld_UserUnknownException* new_helloworld_UserUnknownException();
+helloworld_UserUnknownException* clone_helloworld_UserUnknownException(helloworld_UserUnknownException* other);
+int is_helloworld_UserUnknownException(void* obj);
+
+
+
+typedef etch_string* (*helloworld_say_hello)(void* thisx, helloworld_user* to_whom);
+
+//typedef struct i_mailbox i_mailbox;
+
+typedef i_mailbox* (*helloworld_async_begin_say_hello)(void* thisx, helloworld_user* to_whom);
+
+typedef etch_string* (*helloworld_async_end_say_hello)(void* thisx, i_mailbox*);
+
+/**
+ * i_helloworld
+ * helloworld service interface
+ */
+typedef struct i_helloworld
+{
+ etch_object object;
+
+ /* - - - - - - - - - - -
+ * service virtuals
+ * - - - - - - - - - - -
+ */
+ helloworld_say_hello say_hello;
+
+ /* - - - - - - - - - - -
+ * service data
+ * - - - - - - - - - - -
+ */
+ helloworld_user* user;
+ helloworld_UserUnknownException* UserUnknownException;
+
+} i_helloworld;
+
+i_helloworld* new_helloworld_service_interface();
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* HELLOWORLD_H */
diff --git a/examples/helloworld/c/helloworld_keywords_wireshark.txt b/examples/helloworld/c/helloworld_keywords_wireshark.txt
new file mode 100644
index 0000000..92992e5
--- /dev/null
+++ b/examples/helloworld/c/helloworld_keywords_wireshark.txt
@@ -0,0 +1,24 @@
+
+0x0cab1655,etch.cbinding.test.helloworld.say_hello
+0xc44339d2,etch.cbinding.test.helloworld._result_say_hello
+0xa37cfad4,etch.cbinding.test.helloworld.user
+0xbeae3199,etch.cbinding.test.helloworld.UserUnknownException
+0x5a24fcc0,id
+0xe5879d30,name
+0x2dd371f6,mes
+0x54a712ce,to_whom
+0xef092188,_Etch_RuntimeException
+0x97e30f76,_Etch_AuthException
+0xa53d2e35,_exception
+0x3084deef,_Etch_List
+0x7feae04b,_Etch_Map
+0x82e33e51,_Etch_Set
+0x2b396bcc,_Etch_Datetime
+0x2de1755c,msg
+0x6306b468,_messageId
+0xeda8c9a6,_inReplyTo
+0x8104fdc2,result
+0x5bf76bf9,keys
+0x844e4fc7,values
+0x66001a40,dateTime
+0xee9ff760,keysAndValues
diff --git a/examples/helloworld/c/helloworld_listener_main.c b/examples/helloworld/c/helloworld_listener_main.c
new file mode 100644
index 0000000..5780d37
--- /dev/null
+++ b/examples/helloworld/c/helloworld_listener_main.c
@@ -0,0 +1,130 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+/*
+ * helloworld_listener_main.c
+ */
+
+#include "helloworld_listener_main.h"
+#include "etch_objecttypes.h"
+#include "etch_runtime.h"
+#include "etch_arrayval.h"
+#include "etch_nativearray.h"
+#include "etch_binary_tdo.h"
+#include "etch_general.h"
+
+
+/**
+ * new_helloworld_server
+ * create an individual client's helloworld_server implementation.
+ * this is java binding's newhelloworldServer().
+ * this is called back from helper.new_helper_accepted_server() (java's newServer).
+ * @param p parameter bundle. caller retains.
+ * @return the i_helloworld_server, whose thisx is the helloworld_server_impl.
+ */
+static i_helloworld_server* helloworld_server_create(etch_server_factory* p, etch_session* session)
+{
+ helloworld_remote_client* client = (helloworld_remote_client*) session->client;
+
+ helloworld_server_impl* newserver = new_helloworld_server_impl(client);
+
+ return newserver->helloworld_server_base;
+}
+
+etch_status_t helloworld_listener_start(i_sessionlistener** pplistener, wchar_t* uri, int waitupms)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = helloworld_helper_listener_create(pplistener, uri, NULL, helloworld_server_create);
+ if(etch_status == ETCH_SUCCESS)
+ {
+ etch_status = helloworld_helper_listener_start_wait(*pplistener, waitupms);
+ }
+
+ return etch_status;
+}
+
+etch_status_t helloworld_listener_stop(i_sessionlistener* plistener, int waitupms)
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+
+ etch_status = helloworld_helper_listener_stop_wait(plistener, waitupms);
+ if(etch_status == ETCH_SUCCESS)
+ {
+ helloworld_helper_listener_destroy(plistener);
+ }
+
+ return etch_status;
+}
+
+
+#ifndef NO_ETCH_SERVER_MAIN
+
+/**
+ * main()
+ */
+int main(int argc, char* argv[])
+{
+ etch_status_t etch_status = ETCH_SUCCESS;
+ i_sessionlistener* listener = NULL;
+ int result = -1, is_waitkey = TRUE, waitupms = 4000;
+
+ wchar_t* uri = L"tcp://0.0.0.0:4001";
+
+ etch_config_t* config = NULL;
+ etch_config_create(&config);
+
+ etch_status = etch_runtime_initialize(config);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ return 1;
+ }
+
+ etch_status = helloworld_listener_start(&listener, uri, waitupms);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+
+ // wait for keypress
+ waitkey();
+
+ etch_status = helloworld_listener_stop(listener, waitupms);
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ }
+
+ etch_status = etch_runtime_shutdown();
+ if(etch_status != ETCH_SUCCESS) {
+ // error
+ return 1;
+ }
+ etch_config_destroy(config);
+ // wait for keypress
+ waitkey();
+
+ return 0;
+}
+
+#endif /* NO_ETCH_SERVER_MAIN */
diff --git a/examples/helloworld/c/helloworld_listener_main.h b/examples/helloworld/c/helloworld_listener_main.h
new file mode 100644
index 0000000..3cda593
--- /dev/null
+++ b/examples/helloworld/c/helloworld_listener_main.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+/*
+ * helloworld_listener_main.h
+ * server exe main() private header
+ */
+
+#ifndef HELLOWORLD_LISTENER_MAIN_H
+#define HELLOWORLD_LISTENER_MAIN_H
+
+#include "etch_runtime.h"
+#include "helloworld_helper.h"
+#include "helloworld_server_impl.h"
+#include "helloworld_remote_client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * helloworld_listener_start(pplistener, uri, waitupms)
+ * Start the listener at the given uri, waiting waitupms microseconds for listener startup.
+ * The created listener is saved at address pointed to by pplistener.
+ */
+extern etch_status_t helloworld_listener_start(i_sessionlistener** pplistener, wchar_t* uri, int waitupms);
+
+/*
+ * helloworld_listener_stop(plistener, waitupms)
+ * Stop the listener given by plistener, waiting waitupms microseconds to stop.
+ */
+extern etch_status_t helloworld_listener_stop(i_sessionlistener* plistener, int waitupms);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* HELLOWORLD_LISTENER_MAIN.H */
diff --git a/examples/helloworld/c/helloworld_remote.c b/examples/helloworld/c/helloworld_remote.c
new file mode 100644
index 0000000..2dabb6d
--- /dev/null
+++ b/examples/helloworld/c/helloworld_remote.c
@@ -0,0 +1,269 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#include "helloworld_remote.h"
+#include "etch_url.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+
+unsigned short CLASSID_HELLOWORLD_REMOTE;
+
+int destroy_helloworld_remote (void*);
+#if(0)
+etch_message* etchremote_new_message(helloworld_remote*, etch_type*);
+int etchremote_send(helloworld_remote*, etch_message*);
+int etchremote_begincall(helloworld_remote*, etch_message*, void**);
+int etchremote_endcall (helloworld_remote*, i_mailbox*, etch_type*, void**);
+int etchremote_transport_control (helloworld_remote*, etch_event*, etch_int32*);
+int etchremote_transport_notify (helloworld_remote*, etch_event*);
+etch_object* etchremote_transport_query (helloworld_remote*, etch_object*);
+int etchremote_start_waitup (helloworld_remote*, const int);
+int etchremote_stop_waitdown (helloworld_remote*, const int);
+#endif
+
+
+/* - - - - - - - - - - - - - -
+ * constructors
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * new_helloworld_remote
+ * @param ids delivery service -- caller retains
+ * @param vf helloworld value factory - caller retains
+ * @param ihelloworld optional helloworld service interface -- caller retains
+ */
+helloworld_remote* new_helloworld_remote (void* thisx,
+ i_delivery_service* ids, etch_value_factory* vf, i_helloworld* iservice)
+{
+ helloworld_remote* remote = (helloworld_remote*) new_object (sizeof(helloworld_remote),
+ ETCHTYPEB_REMOTE, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_REMOTE));
+
+ ((etch_object*)remote)->destroy = destroy_helloworld_remote;
+
+ /* helloworld_remote instance data and methods */
+ remote->dsvc = ids;
+ remote->vf = vf;
+ remote->start_waitup = etchremote_start_waitup;
+ remote->stop_waitdown = etchremote_stop_waitdown;
+
+ /* transport methods */
+ remote->transport_control = etchremote_transport_control;
+ remote->transport_notify = etchremote_transport_notify;
+ remote->transport_query = etchremote_transport_query;
+
+ /* remote base */
+ remote->new_message = etchremote_new_message;
+ remote->send = etchremote_send;
+ remote->sendex = etchremote_sendex;
+ remote->begin_call = etchremote_begincall;
+ remote->end_call = etchremote_endcall;
+
+ /* helloworld service */
+ if (iservice)
+ remote->ihelloworld = iservice;
+ else
+ { remote->ihelloworld = new_helloworld_service_interface();
+ remote->is_service_interface_owned = TRUE;
+ }
+ remote->say_hello = remote->ihelloworld->say_hello;
+
+ remote->user = remote->ihelloworld->user;
+
+ return remote;
+}
+
+
+/**
+ * destroy_helloworld_remote()
+ * helloworld_remote destructor.
+ */
+int destroy_helloworld_remote (void* data)
+{
+ helloworld_remote* thisx = (helloworld_remote*)data;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ if (thisx->is_service_interface_owned && thisx->ihelloworld)
+ etch_object_destroy(thisx->ihelloworld);
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
+
+
+/* - - - - - - - - - - - - - -
+ * remote methods
+ * - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchremote_new_message()
+ * instantiates a message to be sent via this.send() or this.begin_call().
+ * @param thisx this remote object.
+ * @param message_type type of message, caller retains.
+ * @return message object, which could wrap an exception.
+ */
+#if(0)
+etch_message* etchremote_new_message ($helper.getRemoteName($intf, $mc)* thisx, etch_type* message_type)
+{
+ etch_message* msg = new_message(message_type, ETCH_DEFSIZE, thisx->vf);
+ return msg;
+}
+#endif
+
+
+/**
+ * etchremote_send()
+ * sends message to recipient without waiting for a response.
+ * @param thisx this remote object.
+ * @param msg message, caller relinquishes.
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_send ($helper.getRemoteName($intf, $mc)* thisx, etch_message* msg)
+{
+ const int result = thisx->dsvc->itm->transport_message(thisx->dsvc, NULL, msg);
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_begincall()
+ * sends message beginning a call sequence.
+ * @param thisx this remote object.
+ * @param msg message, caller relinquishes.
+ * @return in out parameter, a mailbox which can be used to retrieve the response.
+ * @return 0 success, -1 failure.
+ */
+
+#if(0)
+int etchremote_begincall ($helper.getRemoteName($intf, $mc)* thisx, etch_message* msg, i_mailbox** out)
+{
+ const int result = thisx->dsvc->begin_call(thisx->dsvc, msg, out);
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_endcall()
+ * finishes a call sequence by waiting for a response message.
+ * @param thisx this remote object.
+ * @param mbox a mailbox which will be used to read an expected message response.
+ * @param response_type the message type of the expected response.
+ * @return in out parameter, on success, the response.
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_endcall ($helper.getRemoteName($intf, $mc)* thisx, i_mailbox* mbox, etch_type* response_type, etch_object** out)
+{
+ const int result = thisx->dsvc->end_call(thisx->dsvc, mbox, response_type, out);
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_transport_control()
+ * @param evt caller relinquishes
+ * @param value caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_transport_control ($helper.getRemoteName($intf, $mc)* thisx, etch_event* evt, etch_int32* value)
+{
+ const int result = thisx->dsvc->itm->transport_control(thisx->dsvc, evt, value);
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_transport_notify()
+ * @param evt caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_transport_notify ($helper.getRemoteName($intf, $mc)* thisx, etch_event* evt)
+{
+ const int result = thisx->dsvc->itm->transport_notify(thisx->dsvc, evt);
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_transport_query()
+ * @param query caller relinquishes
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+etch_object* etchremote_transport_query ($helper.getRemoteName($intf, $mc)* thisx, etch_object* query)
+{
+ etch_object* resultobj = thisx->dsvc->itm->transport_query(thisx->dsvc, query);
+ return resultobj;
+}
+#endif
+
+
+/**
+ * etchremote_start_waitup()
+ * start the transport and wait for it to come up.
+ * @param thisx this remote object.
+ * @param waitms how long to wait, in milliseconds.
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_start_waitup ($helper.getRemoteName($intf, $mc)* thisx, const int waitms)
+{
+ const int result = thisx->transport_control(thisx,
+ new_etch_event(CLASSID_CONTROL_START_WAITUP, waitms), NULL);
+
+ return result;
+}
+#endif
+
+
+/**
+ * etchremote_stop_waitdown()
+ * stop the transport and wait for it to go down.
+ * @param thisx this remote object.
+ * @param waitms how long to wait, in milliseconds.
+ * @return 0 success, -1 failure.
+ */
+#if(0)
+int etchremote_stop_waitdown ($helper.getRemoteName($intf, $mc)* thisx, const int waitms)
+{
+ const int result = thisx->transport_control(thisx,
+ new_etch_event(CLASSID_CONTROL_STOP_WAITDOWN, waitms), NULL);
+
+ return result;
+}
+#endif
diff --git a/examples/helloworld/c/helloworld_remote.h b/examples/helloworld/c/helloworld_remote.h
new file mode 100644
index 0000000..11dc4c2
--- /dev/null
+++ b/examples/helloworld/c/helloworld_remote.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getRemoteFileNameH($intf, $mc)
+ * helloworld remote.
+ * combines java bindings's RemotePerf, Perf, and RemoteBase.
+ */
+
+#ifndef HELLOWORLD_REMOTE_H
+#define HELLOWORLD_REMOTE_H
+
+#include "helloworld_interface.h"
+#include "etch_remote.h"
+#include "etch_transport.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_HELLOWORLD_REMOTE;
+
+/**
+ * helloworld_remote
+ */
+typedef struct helloworld_remote
+{
+ etch_object object;
+
+ i_helloworld* ihelloworld; /* possibly owned */
+ i_delivery_service* dsvc; /* not owned */
+ etch_value_factory* vf; /* not owned */
+ unsigned char remote_type; /* client or server */
+ unsigned char is_service_interface_owned;
+ unsigned short unused; /* alignment */
+
+ etch_message* (*new_message) (void*, etch_type*);
+ int (*send) (void*, etch_message*);
+ void* (*sendex) (void*, etch_message*);
+ etch_delivsvc_begincall begin_call; /* i_mailbox** out */
+ etch_delvisvc_endcall end_call; /* etch_object** out */
+
+ int (*start_waitup) (void*, const int waitms);
+ int (*stop_waitdown) (void*, const int waitms);
+
+ /* - - - - - - - - - - - - -
+ * transport functionality
+ * - - - - - - - - - - - - -
+ */
+ etch_transport_control transport_control;
+ etch_transport_notify transport_notify;
+ etch_transport_query transport_query;
+ etch_transport_get_session get_session;
+ etch_transport_set_session set_session;
+
+ /* - - - - - - - - - - -
+ * service virtuals
+ * - - - - - - - - - - -
+ */
+ helloworld_say_hello say_hello;
+
+ helloworld_user* user;
+
+ /* - - - - - - - - - - -
+ * private instance data
+ * - - - - - - - - - - -
+ */
+
+} helloworld_remote;
+
+
+helloworld_remote* new_helloworld_remote (void*, i_delivery_service*, etch_value_factory*, i_helloworld*);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+#endif /* HELLOWORLD_REMOTE_H */
diff --git a/examples/helloworld/c/helloworld_remote_client.c b/examples/helloworld/c/helloworld_remote_client.c
new file mode 100644
index 0000000..4a78538
--- /dev/null
+++ b/examples/helloworld/c/helloworld_remote_client.c
@@ -0,0 +1,213 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * $helper.getRemoteName($intf, $suffix).c
+ */
+
+#include "helloworld_remote_client.h"
+#include "helloworld_valufact.h"
+#include "etch_url.h"
+#include "etch_log.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+
+static const char* LOG_CATEGORY = "helloworld_remote_client";
+
+unsigned short CLASSID_HELLOWORLD_REMOTE_CLIENT;
+
+char* HELLOWORLD_ETCHREMC = "REMC";
+
+
+
+/* generated signatures */
+int destroy_helloworld_remote_client (void*);
+
+
+/* - - - - - - - -
+ * instantiation
+ * - - - - - - - -
+ */
+
+/**
+ * new_helloworld_remote_client()
+ * helloworld_remote_client constructor.
+ */
+helloworld_remote_client* new_helloworld_remote_client (void* thisx, etch_session* session, etch_value_factory* vf)
+{
+ helloworld_remote* remote = NULL;
+ helloworld_remote_client* rc = NULL;
+
+ rc = (helloworld_remote_client*) new_object (sizeof(helloworld_remote_client),
+ ETCHTYPEB_REMOTECLIENT, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_REMOTE_CLIENT));
+
+ ((etch_object*)rc)->destroy = destroy_helloworld_remote_client;
+
+ /* we "implement" the service interface here since it may contain client-directed
+ * items, in which case those methods and data are exposed in this object. we do
+ * not expose the service's server-directed methods in the remote client object.
+ */
+ remote = new_helloworld_remote (thisx, session->ds, vf, NULL);
+ remote->remote_type = ETCH_REMOTETYPE_CLIENT;
+
+ rc->remote_base = remote;
+ rc->client_base = new_helloworld_client_base (thisx);
+ rc->session_id = session->session_id;
+
+ rc->vf = (helloworld_valufact*) rc->remote_base->vf;
+
+ /* override client-directed virtuals with implementations here */
+
+ return rc;
+}
+
+
+/**
+ * destroy_helloworld_remote_client()
+ * helloworld_remote_client destructor.
+ */
+int destroy_helloworld_remote_client (void* thisx)
+{
+ helloworld_remote_client* remote = (helloworld_remote_client*)thisx;
+ if (NULL == remote) return -1;
+
+ if (!is_etchobj_static_content(remote))
+ {
+ etch_object_destroy(remote->remote_base);
+ remote->remote_base = NULL;
+ etch_object_destroy(remote->client_base);
+ remote->client_base = NULL;
+ }
+ return destroy_objectex((etch_object*)remote);
+}
+
+
+
+
+
+/**
+ * perf_remote_dispose_mailbox()
+ * dispose of mailbox after use.
+ * this is the common means of disposing of a mailbox when we're done with it.
+ * this would intuitively be part of base class code but we don't have one.
+ * @param thisx the remote server this.
+ * @param pibox pointer to pointer to the mailbox interface, passed indirectly
+ * such that this method can null out the caller's mailbox reference.
+ * @return 0 if mailbox was successfullly closed, otherwise -1.
+ * caller's i_mailbox reference is nulled out regardless of result.
+ */
+int helloworld_remote_client_dispose_mailbox (helloworld_remote_client* thisx, i_mailbox** pibox)
+{
+ int result = 0;
+ i_mailbox* ibox = 0;
+ if (!pibox || !*pibox) return -1;
+ ibox = *pibox;
+ *pibox = NULL; /* null out caller's reference */
+
+ if (0 != (result = ibox->close_read (ibox)))
+ {
+ /* we should not need this failsafe unregister if close_read()
+ * is reliable, since close_read() will do the unregister */
+ i_mailbox_manager* imgr = ibox->manager(ibox);
+ ETCH_LOG(LOG_CATEGORY, ETCH_LOG_ERROR, "could not close mailbox %x\n", ibox);
+ if (imgr) result = imgr->unregister(imgr, ibox);
+ }
+
+ /* mailbox manager does not destroy the unregistered mailbox since it is
+ * owned by whoever registered it, that being us, so we destroy it here.
+ * debug heap issue note: this is/was the spot.
+ */
+ etch_object_destroy(ibox);
+ return result;
+}
+
+/**
+ * perf_remote_get_stubbase()
+ * convenience to return stub base object from remote server object.
+ */
+etch_stub* helloworld_remote_client_get_stubbase (helloworld_remote_client* thisx)
+{
+ etch_stub* stub = NULL;
+ xxxx_either_stub* clistub = (xxxx_either_stub*) thisx->client_factory->stub;
+ stub = clistub? clistub->stub_base: NULL;
+ return stub;
+}
+
+
+/**
+ * helloworld_remote_set_session_notify()
+ * convenience to override remote server's session_notify().
+ * @return the session_notify function that was overridden.
+ */
+etch_session_notify helloworld_remote_client_set_session_notify(helloworld_remote_client* thisx, etch_session_notify newfunc)
+{
+ etch_session_notify oldfunc = NULL;
+ etch_stub* stub = helloworld_remote_client_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_notify;
+ stub->impl_callbacks->_session_notify = newfunc;
+ return oldfunc;
+}
+
+
+/**
+ * helloworld_remote_set_session_control()
+ * convenience to override remote server's session_control().
+ * @return the session_control function that was overridden.
+ */
+etch_session_control helloworld_remote_client_set_session_control(helloworld_remote_client* thisx, etch_session_control newfunc)
+{
+ etch_session_control oldfunc = NULL;
+ etch_stub* stub = helloworld_remote_client_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_control;
+ stub->impl_callbacks->_session_control = newfunc;
+ return oldfunc;
+}
+
+
+/**
+ * helloworld_remote_set_session_query()
+ * convenience to override remote server's session_query().
+ * @return the session_query function that was overridden.
+ */
+etch_session_query helloworld_remote_client_set_session_query(helloworld_remote_client* thisx, etch_session_query newfunc)
+{
+ etch_session_query oldfunc = NULL;
+ etch_stub* stub = helloworld_remote_client_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_query;
+ stub->impl_callbacks->_session_query = newfunc;
+ return oldfunc;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * remote procedure call implementations
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
diff --git a/examples/helloworld/c/helloworld_remote_client.h b/examples/helloworld/c/helloworld_remote_client.h
new file mode 100644
index 0000000..5b7d6e5
--- /dev/null
+++ b/examples/helloworld/c/helloworld_remote_client.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * helloworld_remote_client.h
+ */
+
+#ifndef HELLOWORLD_REMOTE_CLIENT_H
+#define HELLOWORLD_REMOTE_CLIENT_H
+
+#include "helloworld_remote.h"
+#include "helloworld_client.h"
+#include "etch_transport.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_HELLOWORLD_REMOTE_CLIENT;
+
+/**
+ * helloworld_remote_client
+ */
+typedef struct helloworld_remote_client
+{
+ etch_object object;
+
+ i_helloworld_client* client_base; /* owned */
+ helloworld_remote* remote_base; /* owned */
+ etch_client_factory* client_factory; /* owned */
+ default_value_factory* vf; /* owned by base */
+ int session_id;
+
+ /* toward-client virtuals go here */
+
+/*
+ helloworld_user* user;
+*/
+
+
+ /* private, generally. since unit tests invoke async begin and end,
+ * we must expose them either as virtuals or as external references.
+ */
+
+
+
+
+} helloworld_remote_client;
+
+/* constructor */
+helloworld_remote_client* new_helloworld_remote_client (void*, etch_session*, etch_value_factory*);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* HELLOWORLD_REMOTE_CLIENT_H */
diff --git a/examples/helloworld/c/helloworld_remote_server.c b/examples/helloworld/c/helloworld_remote_server.c
new file mode 100644
index 0000000..2ddf094
--- /dev/null
+++ b/examples/helloworld/c/helloworld_remote_server.c
@@ -0,0 +1,309 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * helloworld_remote_server.c
+ * generated remote procedure calls.
+ */
+
+#include "helloworld_remote_server.h"
+#include "etch_plain_mailbox_manager.h"
+#include "etch_svcobj_masks.h"
+#include "etch_objecttypes.h"
+#include "etch_exception.h"
+#include "etch_url.h"
+#include "etch_log.h"
+#include "etch_general.h"
+
+unsigned short CLASSID_HELLOWORLD_REMOTE_SERVER;
+
+char* HELLOWORLD_ETCHREMS = "REMS";
+
+
+etch_string* helloworld_remote_server_say_hello (void* thisAsVoid, helloworld_user* to_whom);
+i_mailbox* helloworld_remote_begin_server_say_hello (void* thisAsVoid, helloworld_user* to_whom);
+etch_string* helloworld_remote_end_server_say_hello(void*, i_mailbox*);
+
+
+int destroy_helloworld_remote_server (void*);
+
+/* - - - - - - - - - -
+ * instantiation etc.
+ * - - - - - - - - - -
+ */
+
+/**
+ * new_helloworld_remote_server()
+ * helloworld_remote_server constructor.
+ * @param thisx owner, optional, null in practice.
+ * @param ids delivery service interface, caller retains.
+ * @param vf service specific value factory, caller retains.
+ */
+helloworld_remote_server* new_helloworld_remote_server (void* thisx, i_delivery_service* ids, etch_value_factory* vf)
+{
+ helloworld_remote_server* rs = (helloworld_remote_server*) new_object (sizeof(helloworld_remote_server),
+ ETCHTYPEB_REMOTESERVER, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_REMOTE_SERVER));
+
+ ((etch_object*)rs)->destroy = destroy_helloworld_remote_server;
+
+ rs->remote_base = new_helloworld_remote (thisx, ids, vf, NULL);
+ rs->remote_base->remote_type = ETCH_REMOTETYPE_SERVER;
+
+ /* 2/3/09 now passing perf interface not owned by server_base */
+ rs->server_base = new_helloworld_remote_server_base (thisx, rs->remote_base->ihelloworld);
+
+ rs->vf = (helloworld_valufact*) rs->remote_base->vf;
+
+ rs->say_hello = helloworld_remote_server_say_hello;
+
+ rs->async_begin_say_hello = helloworld_remote_begin_server_say_hello;
+ rs->async_end_say_hello = helloworld_remote_end_server_say_hello;
+
+ return rs;
+}
+
+/**
+ * destroy_helloworld_server_base()
+ * i_helloworld_server destructor.
+ */
+int destroy_helloworld_remote_server (void* thisAsVoid)
+{
+ helloworld_remote_server* thisx = (helloworld_remote_server*)thisAsVoid;
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ etch_object_destroy(thisx->remote_base);
+ thisx->remote_base = NULL;
+ etch_object_destroy(thisx->server_base);
+ thisx->server_base = NULL;
+ etch_object_destroy(thisx->client_factory);
+ thisx->client_factory = NULL;
+ }
+
+ return destroy_objectex((etch_object*)thisx);
+}
+
+
+/**
+ * perf_remote_dispose_mailbox()
+ * dispose of mailbox after use.
+ * this is the common means of disposing of a mailbox when we're done with it.
+ * this would intuitively be part of base class code but we don't have one.
+ * @param thisx the remote server this.
+ * @param pibox pointer to pointer to the mailbox interface, passed indirectly
+ * such that this method can null out the caller's mailbox reference.
+ * @return 0 if mailbox was successfullly closed, otherwise -1.
+ * caller's i_mailbox reference is nulled out regardless of result.
+ */
+int helloworld_remote_server_dispose_mailbox (helloworld_remote_server* thisx, i_mailbox** pibox)
+{
+ int result = 0;
+ i_mailbox* ibox = 0;
+ if (!pibox || !*pibox) return -1;
+ ibox = *pibox;
+ *pibox = NULL; /* null out caller's reference */
+
+ if (0 != (result = ibox->close_read (ibox)))
+ {
+ /* we should not need this failsafe unregister if close_read()
+ * is reliable, since close_read() will do the unregister */
+ i_mailbox_manager* imgr = ibox->manager(ibox);
+ ETCH_LOG(HELLOWORLD_ETCHREMS, ETCH_LOG_ERROR, "could not close mailbox %x\n", ibox);
+ if (imgr) result = imgr->unregister(imgr, ibox);
+ }
+
+ /* mailbox manager does not destroy the unregistered mailbox since it is
+ * owned by whoever registered it, that being us, so we destroy it here.
+ * debug heap issue note: this is/was the spot.
+ */
+ etch_object_destroy(ibox);
+ return result;
+}
+
+etch_stub* helloworld_remote_server_get_stubbase (helloworld_remote_server* thisx)
+{
+ etch_stub* stub = NULL;
+ xxxx_either_stub* clistub = (xxxx_either_stub*) thisx->client_factory->stub;
+ stub = clistub? clistub->stub_base: NULL;
+ return stub;
+}
+
+
+/**
+ * helloworld_remote_set_session_notify()
+ * convenience to override remote server's session_notify().
+ * @return the session_notify function that was overridden.
+ */
+etch_session_notify helloworld_remote_server_set_session_notify(helloworld_remote_server* thisx, etch_session_notify newfunc)
+{
+ etch_session_notify oldfunc = NULL;
+ etch_stub* stub = helloworld_remote_server_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_notify;
+ stub->impl_callbacks->_session_notify = newfunc;
+ return oldfunc;
+}
+
+
+/**
+ * helloworld_remote_set_session_control()
+ * convenience to override remote server's session_control().
+ * @return the session_control function that was overridden.
+ */
+etch_session_control helloworld_remote_server_set_session_control(helloworld_remote_server* thisx, etch_session_control newfunc)
+{
+ etch_session_control oldfunc = NULL;
+ etch_stub* stub = helloworld_remote_server_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_control;
+ stub->impl_callbacks->_session_control = newfunc;
+ return oldfunc;
+}
+
+
+/**
+ * helloworld_remote_set_session_query()
+ * convenience to override remote server's session_query().
+ * @return the session_query function that was overridden.
+ */
+etch_session_query helloworld_remote_server_set_session_query(helloworld_remote_server* thisx, etch_session_query newfunc)
+{
+ etch_session_query oldfunc = NULL;
+ etch_stub* stub = helloworld_remote_server_get_stubbase(thisx);
+ if (NULL == stub || NULL == stub->impl_callbacks) return NULL;
+ oldfunc = stub->impl_callbacks->_session_query;
+ stub->impl_callbacks->_session_query = newfunc;
+ return oldfunc;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * remote procedure call implementations
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+
+/* - - - - - - - - - - -
+ * helloworld.say_hello()
+ * - - - - - - - - - - -
+ */
+
+/**
+ * helloworld_remote_begin_say_hello()
+ * helloworld.say_hello async start
+ * TODO: doc generation
+ */
+i_mailbox* helloworld_remote_begin_server_say_hello(void* thisAsVoid, helloworld_user* to_whom)
+{
+ helloworld_remote_server* thisx = (helloworld_remote_server*)thisAsVoid;
+ int _result = 0;
+ i_mailbox* _mbox = NULL;
+ etch_message* _msg = NULL;
+ etch_type* _msgtype = helloworld_valufact_get_static()->_mt_helloworld_say_hello;
+
+ do
+ {
+ _msg = thisx->remote_base->new_message (thisx->remote_base, _msgtype);
+ if (!_msg) break;
+
+ _result = message_putc (_msg, helloworld_valufact_get_static()->_mf_helloworld_to_whom, (void**)&to_whom);
+ if (to_whom != NULL && 0 != _result) break;
+
+ /* fyi msg memory is relinquished here regardless of result */
+ _result = thisx->remote_base->begin_call(thisx->remote_base, _msg, (void**)&_mbox);
+ _msg = NULL;
+
+ } while(0);
+
+ /* destroy any unrelinquished objects */
+ etch_object_destroy(to_whom);
+ to_whom = NULL;
+ etch_object_destroy(_msg);
+ _msg = NULL;
+
+ return _mbox;
+}
+
+/**
+ * helloworld_remote_end__say_hello()
+ * _say_hello async end (read result from mailbox and return result)
+ * @param thisx this.
+ * @param mbox caller relinquishes
+ * @return etch_int32* result of add, caller owns.
+ */
+etch_string* helloworld_remote_end_server_say_hello (void* thisAsVoid, i_mailbox* ibox)
+{
+ helloworld_remote_server* thisx = (helloworld_remote_server*)thisAsVoid;
+ etch_string* _resobj = NULL;
+ int _result = -1;
+ etch_type* _restype = helloworld_valufact_get_static()->_mt_helloworld__result_say_hello;
+
+ if(ibox == NULL) {
+ return NULL;
+ }
+
+ thisx->remote_base->end_call(thisx->remote_base, ibox, _restype, (void**)&_resobj);
+
+ _result = helloworld_remote_server_dispose_mailbox (thisx, &ibox);
+ if(_result) {
+ etch_exception* excp = new_etch_exception_from_errorcode(ETCH_ERROR);
+ etch_exception_set_message(excp,new_stringw(L"can not dispose mailbox."));
+ return (etch_string*)excp;
+ }
+
+ return _resobj;
+}
+
+/**
+ * helloworld_remote_say_hello
+ * helloworld.say_hello remote method call.
+ * instantiates a mailbox, sends perf.add message, waits for result to arrive
+ * in mailbox, disposes mailbox, and returns object containing add() result.
+ * @param thisx this.
+ * @param x etch_int32* caller relinquishes.
+ * @param y etch_int32* caller relinquishes.
+ * @return etch_int32* result of add, caller owns.
+ */
+etch_string* helloworld_remote_server_say_hello(void* thisAsVoid, helloworld_user* to_whom)
+{
+ helloworld_remote_server* thisx = (helloworld_remote_server*)thisAsVoid;
+ etch_string* _resultobj = NULL;
+
+ i_mailbox* _mbox = helloworld_remote_begin_server_say_hello(thisx, to_whom);
+
+ if(_mbox == NULL){
+ etch_exception* excp = new_etch_exception_from_errorcode(ETCH_EIO);
+ etch_exception_set_message(excp,new_stringw(L"can not create mailbox, connection could be down."));
+ return (etch_string*)excp;
+ }
+
+ _resultobj = helloworld_remote_end_server_say_hello (thisx, _mbox);
+
+ return _resultobj;
+}
+
diff --git a/examples/helloworld/c/helloworld_remote_server.h b/examples/helloworld/c/helloworld_remote_server.h
new file mode 100644
index 0000000..6f8ea28
--- /dev/null
+++ b/examples/helloworld/c/helloworld_remote_server.h
@@ -0,0 +1,92 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * helloworld_remote_server.h
+ * generated remote procedure calls.
+ */
+
+#ifndef HELLOWORLD_REMOTE_SERVER_H
+#define HELLOWORLD_REMOTE_SERVER_H
+
+#include "helloworld_remote.h"
+#include "helloworld_server.h"
+#include "helloworld_valufact.h"
+#include "etch_transport.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_HELLOWORLD_REMOTE_SERVER;
+
+
+/**
+ * helloworld_remote_server
+ */
+typedef struct helloworld_remote_server
+{
+ etch_object object;
+
+ i_helloworld_server* server_base; /* owned */
+ helloworld_remote* remote_base; /* owned */
+ etch_client_factory* client_factory; /* owned */
+ helloworld_valufact* vf; /* owned by base */
+ int server_id;
+
+ helloworld_say_hello say_hello;
+
+/*
+ helloworld_user* user;
+*/
+
+ /* private, generally. since unit tests invoke async begin and end,
+ * we must expose them either as virtuals or as external references.
+ */
+ helloworld_async_begin_say_hello async_begin_say_hello;
+
+ helloworld_async_end_say_hello async_end_say_hello;
+
+} helloworld_remote_server;
+
+
+/* constructor */
+helloworld_remote_server* new_helloworld_remote_server(void*, i_delivery_service*, etch_value_factory*);
+
+int helloworld_remote_dispose_mailbox(helloworld_remote_server*, i_mailbox**);
+
+/* convenience methods to override client's session interface methods */
+etch_stub* helloworld_remote_get_stubbase (helloworld_remote_server*);
+etch_session_notify helloworld_remote_set_session_notify(helloworld_remote_server*, etch_session_notify);
+etch_session_control helloworld_remote_set_session_control(helloworld_remote_server*, etch_session_control);
+etch_session_query helloworld_remote_set_session_query(helloworld_remote_server*, etch_session_query);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* HELLOWORLD_REMOTE_SERVER_H */
diff --git a/examples/helloworld/c/helloworld_server.c b/examples/helloworld/c/helloworld_server.c
new file mode 100644
index 0000000..10a7ff1
--- /dev/null
+++ b/examples/helloworld/c/helloworld_server.c
@@ -0,0 +1,150 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#include "helloworld_server.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_url.h"
+
+unsigned short CLASSID_HELLOWORLD_SERVER_BASE;
+
+
+int destroy_helloworld_server_via_base(void*);
+
+static int helloworld_server_id_farm;
+
+/* - - - - - - - - - - - - - -
+ * constructors
+ * - - - - - - - - - - - - - -
+ */
+/**
+ * new_helloworld_server_base()
+ * @param implobj not interpreted
+ * @param psi a helloworld service interface. if supplied, caller retains,
+ * otherwise a service interface is instantiated and owned here.
+ */
+i_helloworld_server* new_helloworld_server_base(void* implobj, i_helloworld* psi)
+{
+ i_helloworld_server* ips = (i_helloworld_server*) new_object (sizeof(i_helloworld_server),
+ ETCHTYPEB_EXESERVERBASE, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_SERVER_BASE));
+
+ /* the server impl is destroyed via this base object. the virtual destructor we assign
+ * here will call the impl object's virtual destructor, which will directly call a
+ * non-virtual destructor for the base object.
+ */
+
+ ((etch_object*)ips)->destroy = destroy_helloworld_server_via_base;
+
+ ips->thisx = implobj; /* null passed thru from client main */
+
+ if (psi)
+ ips->ihelloworld = psi;
+ else
+ { ips->ihelloworld = new_helloworld_service_interface();
+ ips->is_service_interface_owned = TRUE;
+ }
+
+ ips->say_hello = ips->ihelloworld->say_hello;
+
+ ips->user = ips->ihelloworld->user;
+
+ ips->iobjsession = new_default_objsession_interface (ips);
+ ips->_session_control = ips->iobjsession->_session_control;
+ ips->_session_notify = ips->iobjsession->_session_notify;
+ ips->_session_query = ips->iobjsession->_session_query;
+
+ ips->server_id = ++helloworld_server_id_farm;
+
+ return ips;
+}
+
+/**
+ * new_helloworld_remote_server_base()
+ * constructor for server base when host is a remote server.
+ * the server base destructor in this case destroys only itself.
+ * @param psi a helloworld service interface, if supplied, caller retains.
+ * may be null.
+ */
+i_helloworld_server* new_helloworld_remote_server_base (void* implobj, i_helloworld* psi)
+{
+ i_helloworld_server* ips = new_helloworld_server_base (implobj, psi);
+ ((etch_object*)ips)->destroy = destroy_helloworld_server_base;
+ return ips;
+}
+
+/**
+ * destroy_helloworld_server_base()
+ * i_helloworld_server destructor.
+ */
+int destroy_helloworld_server_base (void* data)
+{
+
+ i_helloworld_server* ips = (i_helloworld_server*)data;
+ if (NULL == ips) return -1;
+
+ if (!is_etchobj_static_content(ips))
+ {
+ if (ips->is_service_interface_owned){
+ //ETCHOBJ_DESTROY(ips->ihelloworld);
+ if(ips->ihelloworld){
+ etch_object_destroy(ips->ihelloworld);
+ }
+ ips->ihelloworld = NULL;
+
+ }
+
+ etch_free(ips->iobjsession);
+ }
+
+ return destroy_objectex((etch_object*)ips);
+}
+
+
+/**
+ * destroy_helloworld_server_via_base()
+ * destructor for helloworld_server_impl via i_helloworld_server.
+ */
+int destroy_helloworld_server_via_base (void* data)
+{
+ i_helloworld_server* ips = (i_helloworld_server*)data;
+ if (NULL == ips) return -1;
+
+ if (!is_etchobj_static_content(ips))
+ {
+ /* serverimpl dtor will call base dtor (destroy_helloworld_server_base) */
+ etch_object* serverimpl = (etch_object*) ips->thisx;
+ ETCH_ASSERT(is_etch_serverimpl(serverimpl));
+ //ETCHOBJ_DESTROY(serverimpl);
+ if(serverimpl){
+ etch_object_destroy(serverimpl);
+ }
+ serverimpl = NULL;
+ }
+
+ return 0;
+}
+
diff --git a/examples/helloworld/c/helloworld_server.h b/examples/helloworld/c/helloworld_server.h
new file mode 100644
index 0000000..5114e6a
--- /dev/null
+++ b/examples/helloworld/c/helloworld_server.h
@@ -0,0 +1,104 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * helloworld_server.h
+ * helloworld client interface.
+ * combines java bindings's helloworldServer and BasehelloworldServer
+ */
+
+#ifndef HELLOWORLD_SERVER_H
+#define HELLOWORLD_SERVER_H
+
+#include "helloworld_interface.h"
+#include "etch_sessionint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_HELLOWORLD_SERVER_BASE;
+
+//typedef struct helloworld_server_impl helloworld_server_impl;
+
+/**
+ * i_helloworld_server
+ * helloworld server base interface
+ */
+typedef struct i_helloworld_server
+{
+ etch_object object;
+
+ struct helloworld_server_impl* thisx;
+ i_helloworld* ihelloworld;
+
+
+ int session_id;
+ unsigned char is_service_interface_owned;
+ unsigned char unused[3];
+
+ /* - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - -
+ */
+ i_objsession* iobjsession;
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - -
+ * service virtuals
+ * - - - - - - - - - - -
+ */
+ helloworld_say_hello say_hello;
+
+ helloworld_say_hello async_say_hello;
+
+ /* - - - - - - - - - - -
+ * service data
+ * - - - - - - - - - - -
+ */
+ helloworld_user* user;
+
+ /* - - - - - - - - - - -
+ * private instance data
+ * - - - - - - - - - - -
+ */
+ int server_id;
+
+} i_helloworld_server;
+
+i_helloworld_server* new_helloworld_server_base (void* implobj, i_helloworld*);
+i_helloworld_server* new_helloworld_remote_server_base (void* implobj, i_helloworld*);
+int destroy_helloworld_server_base (void*);
+
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* HELLOWORLD_SERVER_H */
diff --git a/examples/helloworld/c/helloworld_server_impl.c b/examples/helloworld/c/helloworld_server_impl.c
new file mode 100644
index 0000000..8c5f46c
--- /dev/null
+++ b/examples/helloworld/c/helloworld_server_impl.c
@@ -0,0 +1,95 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#include "helloworld_server_impl.h"
+#include "etch_url.h"
+#include "etch_arrayval.h"
+#include "etch_binary_tdo.h"
+#include "etch_exception.h"
+#include "etch_general.h"
+#include "etch_log.h"
+
+#include <stdio.h>
+
+unsigned short CLASSID_HELLOWORLD_SERVER_IMPL;
+
+
+char* HELLOWORLD_ETCHSIMP = "SIMP";
+
+/* generated signatures */
+int destroy_helloworld_server_implx(helloworld_server_impl*);
+helloworld_server_impl* init_helloworld_server_impl(struct helloworld_remote_client*, etch_object_destructor);
+
+
+/* - - - - - - - -
+ * instantiation
+ * - - - - - - - -
+ */
+
+/**
+ * new_helloworld_server_impl()
+ * helloworld_server_impl constructor.
+ * add your custom initialization and virtual method overrides here.
+ */
+helloworld_server_impl* new_helloworld_server_impl(struct helloworld_remote_client* client)
+{
+ helloworld_server_impl* pserver /* allocate object and assign default virtuals */
+ = init_helloworld_server_impl(client, destroy_helloworld_server_implx);
+ i_helloworld_server* pserver_base = pserver->helloworld_server_base;
+
+ pserver_base->object.class_id = get_dynamic_classid_unique(&CLASSID_HELLOWORLD_SERVER_IMPL);
+ /* add virtual method overrides, if any, here */
+ //pserver->xxx = implementation
+
+ return pserver;
+}
+
+
+/**
+ * destroy_helloworld_server_implx()
+ * destructor for any user allocated memory.
+ * this code is invoked by the private perf_client_impl destructor,
+ * via perf_client.destroyex(). add code here to destroy any memory you
+ * may have allocated for your custom perf_client implementation.
+ */
+int destroy_helloworld_server_implx(helloworld_server_impl* thisx)
+{
+ /* * * add custom destruction here * * */
+ //etch_free(thisx->exampleobj);
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - -
+ * session interface method overrides
+ * - - - - - - - - - - - - - - - - - - -
+ */
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * implementations of helloworld_server messages from server, if any
+ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
diff --git a/examples/helloworld/c/helloworld_server_impl.h b/examples/helloworld/c/helloworld_server_impl.h
new file mode 100644
index 0000000..52bb064
--- /dev/null
+++ b/examples/helloworld/c/helloworld_server_impl.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#ifndef HELLOWORLD_SERVER_IMPL_H
+#define HELLOWORLD_SERVER_IMPL_H
+
+#include "helloworld_server.h"
+#include "etch_transport.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_HELLOWORLD_SERVER_IMPL;
+
+//typedef struct helloworld_remote_client helloworld_remote_client;
+
+/**
+ * helloworld_server_impl
+ * your custom implementation of helloworld_server. add methods here
+ * to provide implementations of messages from the client, if any.
+ */
+typedef struct helloworld_server_impl
+{
+ etch_object object;
+
+ i_helloworld_server* helloworld_server_base; /* owned */
+ i_helloworld* ihelloworld; /* not owned */
+ struct helloworld_remote_client* client; /* not owned */
+
+ int (*destroyex) (void*); /* user memory destructor */
+
+ /* - - - - - - - - - - - -
+ * objsession
+ * - - - - - - - - - - - -
+ */
+ i_objsession* iobjsession; /* owned by base */
+ /* note that iobjsession->thisx is set to this helloworld_server_impl* */
+ etch_session_control _session_control;
+ etch_session_notify _session_notify;
+ etch_session_query _session_query;
+
+ /* - - - - - - - - - - - -
+ * base service virtuals
+ * - - - - - - - - - - - -
+ */
+ helloworld_say_hello say_hello;
+
+ void* context;
+
+
+} helloworld_server_impl;
+
+/* constructor */
+helloworld_server_impl* new_helloworld_server_impl (struct helloworld_remote_client*);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* HELLOWORLD_SERVER_IMPL_H */
diff --git a/examples/helloworld/c/helloworld_server_implx.c b/examples/helloworld/c/helloworld_server_implx.c
new file mode 100644
index 0000000..c6ae57f
--- /dev/null
+++ b/examples/helloworld/c/helloworld_server_implx.c
@@ -0,0 +1,103 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * helloworld_server_implx.c
+ * $helper.getImplName functions which would ordinarily not be subject to edit.
+ */
+
+#include "helloworld_server_impl.h"
+#include "helloworld_remote_client.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_url.h"
+
+int destroy_helloworld_server_impl(helloworld_server_impl*);
+
+/* - - - - - - - - - - - - - - - - - - - - - - - -
+ *helloworld_server_impl private construction / destruction
+ * - - - - - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * init_helloworld_server_impl()
+ * called by helloworld_server_impl constructor to instantiate server implementation
+ * object and initialize with default virtuals.
+ * @param client the remote client, not owned.
+ * @param usermem_dtor destructor for any custom memory allocations.
+ */
+helloworld_server_impl* init_helloworld_server_impl(helloworld_remote_client* client,
+ etch_object_destructor usermem_dtor)
+{
+ helloworld_server_impl* pserver = (helloworld_server_impl*) new_object (sizeof(helloworld_server_impl),
+ ETCHTYPEB_EXESERVERIMPL, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_SERVER_IMPL));
+
+ pserver->client = client; /* not owned */
+ pserver->object.destroy = destroy_helloworld_server_impl; /* private destructor */
+ pserver->destroyex = usermem_dtor; /* user memory destructor */
+
+ /* instantiate base and copy virtuals, if any, to this object */
+ pserver->helloworld_server_base = new_helloworld_server_base(pserver, NULL); /* owned */
+
+ pserver->ihelloworld = pserver->helloworld_server_base->ihelloworld;
+
+
+ pserver->say_hello = pserver->helloworld_server_base->say_hello;
+
+
+ pserver->iobjsession = pserver->helloworld_server_base->iobjsession;
+ pserver->iobjsession->thisx = pserver; /* set implementor reference */
+ pserver->_session_control = pserver->helloworld_server_base->_session_control;
+ pserver->_session_notify = pserver->helloworld_server_base->_session_notify;
+ pserver->_session_query = pserver->helloworld_server_base->_session_query;
+
+ return pserver;
+}
+
+/**
+ * destroy_perf_server_impl()
+ * perf_server_impl private destructor.
+ * calls back to user destructor to effect cleanup of any perf_server_impl
+ * memory which may have been allocated in custom code added by user.
+ */
+int destroy_helloworld_server_impl (helloworld_server_impl* thisx)
+{
+ if (NULL == thisx) return -1;
+
+ if (!is_etchobj_static_content(thisx))
+ {
+ if(thisx->destroyex)
+ { /* call back to user memory destructor */
+ thisx->destroyex(thisx);
+ }
+ if(thisx->helloworld_server_base)
+ {
+ destroy_helloworld_server_base(thisx->helloworld_server_base);
+ }
+ }
+
+ return destroy_objectex(thisx);
+}
diff --git a/examples/helloworld/c/helloworld_server_stub.c b/examples/helloworld/c/helloworld_server_stub.c
new file mode 100644
index 0000000..c69771e
--- /dev/null
+++ b/examples/helloworld/c/helloworld_server_stub.c
@@ -0,0 +1,148 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * helloworld_server_stub.c
+ */
+
+#include "helloworld_server.h"
+#include "helloworld_server_stub.h"
+#include "helloworld_valufact.h"
+
+#include "etch_url.h"
+#include "etch_objecttypes.h"
+#include "etch_svcobj_masks.h"
+#include "etch_general.h"
+
+#include "etch_exception.h"
+#include "etch_log.h"
+
+unsigned short CLASSID_HELLOWORLD_SERVER_STUB;
+
+char* HELLOWORLD_HELLOWORLD_ETCHSTBI = "STBI";
+
+int destroy_helloworld_server_stub(void*);
+
+
+/* - - - - - - - - - - -
+ * stub helper methods
+ * - - - - - - - - - - -
+ */
+
+/**
+ * helloworld_stub_run_say_hello
+ */
+int helloworld_stub_run_server_say_hello(etch_stub* stub, i_delivery_service* dsvc, void* obj, etch_who* whofrom, etch_message* msg)
+{
+ i_helloworld_server* server = (i_helloworld_server*)obj;
+ int returnCode = 0;
+ helloworld_valufact_impl* pvfi = NULL;
+ helloworld_valufact* pvf = NULL;
+ struct helloworld_server_impl* impl = NULL;
+
+ /* objects specific to helloworld_server.$name.name()() */
+ etch_field* key_to_whom = NULL;
+ helloworld_user* val_to_whom = NULL;
+ etch_string* resultobj = NULL;
+ etchstub_validate_args (stub, dsvc, msg, server, &pvf, (void**)&pvfi, (void**)&impl);
+
+ key_to_whom = helloworld_valufact_get_static()->_mf_helloworld_to_whom;
+ ETCH_ASSERT(key_to_whom);
+
+ val_to_whom = (helloworld_user*) message_remove(msg, key_to_whom);
+
+ /* execute the service method */
+ resultobj = server->say_hello (impl, val_to_whom);
+
+ returnCode = etchstub_send_reply (stub, dsvc, whofrom, msg, (void*) resultobj, resultobj != NULL);
+ if(!returnCode){
+ etch_object_destroy(msg);
+ }
+ return returnCode;
+}
+
+/* - - - - - - - - -
+ * constructors
+ * - - - - - - - - -
+ */
+
+/**
+ * new_helloworld_server_stub()
+ * @param p serv factory parameter bundle, caller retains.
+ *
+ * this constructor is called on the server via callback from the listener
+ * socket accept handler <transportfactory>_session_accepted, via the new_server
+ * function pointer to perf_helper.new_helper_accepted_server().
+ * java binding passes this constructor the delivery service, however we pass the
+ * etch_server_factory parameter bundle, (i_sessionlistener.server_params),
+ * i_sessionlistener being the set session interface of etch_tcp_server.
+ */
+helloworld_server_stub* new_helloworld_server_stub(etch_server_factory* p, etch_session* session)
+{
+ i_delivery_service* ids = session->ds;
+ etch_threadpool *qp = p->qpool, *fp = p->fpool;
+
+ helloworld_server_stub* mystub = new_serverstub_init (session->server,
+ sizeof(helloworld_server_stub), destroy_helloworld_server_stub, ids, qp, fp, p);
+
+ ((etch_object*)mystub)->class_id = get_dynamic_classid_unique(&CLASSID_HELLOWORLD_SERVER_STUB);
+ mystub->session_id = session->session_id;
+
+ /* initialize service-specific methods and data here. this facility may
+ * likely prove unecessary, as this is generated code, in which case we
+ * can remove any associated comments from code and headers. */
+ //mystub->my_example_obj = etch_malloc(128, 0); /* example custom alloc */
+
+ /* set stub "helper" methods (run() procedures for each message type) */
+ etchtype_set_type_stubhelper(helloworld_valufact_get_static()->_mt_helloworld_say_hello, helloworld_stub_run_server_say_hello);
+
+ return mystub;
+}
+
+/**
+ * is_helloworld_server_stub()
+ */
+int is_helloworld_server_stub(void* obj)
+{
+ return obj && ((etch_object*)obj)->class_id == CLASSID_HELLOWORLD_SERVER_STUB;
+}
+
+/**
+ * destroy_def_helloworld_server_stub()
+ * helloworld_server_stub user-allocated memory destructor.
+ * called back from private object destructor destroy_stub_object().
+ * if you explicitly allocate memory in the client stub object, destroy it here.
+ */
+int destroy_helloworld_server_stub(void* data)
+{
+ /*
+ helloworld_server_stub* mystub = (helloworld_server_stub*)data;
+ free custom memory allocations here */
+ //etch_free(mystub->my_example_obj); /* free example alloc */
+
+ return 0;
+}
diff --git a/examples/helloworld/c/helloworld_server_stub.h b/examples/helloworld/c/helloworld_server_stub.h
new file mode 100644
index 0000000..6b430b6
--- /dev/null
+++ b/examples/helloworld/c/helloworld_server_stub.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+#ifndef HELLOWORLD_SERVER_STUB_H
+#define HELLOWORLD_SERVER_STUB_H
+
+#include "etch_stub.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned short CLASSID_HELLOWORLD_SERVER_STUB;
+
+/**
+ * helloworld_server_stub
+ */
+typedef struct helloworld_server_stub
+{
+ etch_object object;
+
+ etch_stub* stub_base;
+
+ int session_id; /* client session to which stub belongs */
+ int (*destroyex) (void*); /* user memory destructor */
+
+ /* - - - - - - - - - - - - - - - - -
+ * server_directed service virtuals
+ * - - - - - - - - - - - - - - - - -
+ */
+
+
+ /* - - - - - - - - - - - - - - - - -
+ * service-specific allocations
+ * - - - - - - - - - - - - - - - - -
+ */
+ // add here
+
+} helloworld_server_stub;
+
+helloworld_server_stub* new_helloworld_server_stub (etch_server_factory*, etch_session*);
+int is_helloworld_server_stub(void* obj);
+int destroy_helloworld_server_stub (void*);
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif /* PERF_SERVER_STUB_H */
+
diff --git a/examples/helloworld/c/helloworld_valufact.c b/examples/helloworld/c/helloworld_valufact.c
new file mode 100644
index 0000000..fbad16d
--- /dev/null
+++ b/examples/helloworld/c/helloworld_valufact.c
@@ -0,0 +1,518 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+/*
+ * helloworld_valufact.c
+ * helloworld service value factory
+ */
+
+#include "helloworld_valufact.h"
+#include "helloworld_interface.h"
+#include "etch_serializer.h"
+#include "etch_exception.h"
+#include "etch_objecttypes.h"
+#include "etch_general.h"
+#include "etch_map.h"
+#include "etch_runtime.h"
+
+unsigned short CLASSID_HELLOWORLD_VALUFACT_IMPL;
+
+static helloworld_valufact_statics* _g_helloworld_valufact_statics = NULL;
+
+/* constructors */
+etch_arraylist* helloworld_valufact_get_types(helloworld_valufact*);
+
+void helloworld_valufact_free_statics();
+
+/* initializers */
+static int helloworld_valufact_init_static_fields();
+static int helloworld_valufact_init_static_types();
+static int helloworld_valufact_init_static_parameters();
+static int helloworld_valufact_init_static_serializers();
+
+/* user serializer */
+etch_serializer* new_helloworld_user_serializer(etch_type*, etch_field*);
+etch_object* etchserializer_helloworld_user_export_value(etch_serializer*, etch_object*);
+etch_object* etchserializer_helloworld_user_import_value(etch_serializer*, etch_object*);
+/* UserUnknownException serializer */
+etch_serializer* new_helloworld_UserUnknownException_serializer(etch_type*, etch_field*);
+etch_object* etchserializer_helloworld_UserUnknownException_export_value(etch_serializer*, etch_object*);
+etch_object* etchserializer_helloworld_UserUnknownException_import_value(etch_serializer*, etch_object*);
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * static constructors/destructors
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+etch_status_t helloworld_etch_runtime_shutdown_hook_func()
+{
+ if(_g_helloworld_valufact_statics) {
+ helloworld_valufact_free_statics();
+ }
+ return ETCH_SUCCESS;
+}
+
+helloworld_valufact_statics* helloworld_valufact_get_static(){
+ if(_g_helloworld_valufact_statics == NULL){
+ _g_helloworld_valufact_statics = malloc(sizeof(helloworld_valufact_statics));
+ memset(_g_helloworld_valufact_statics ,0 ,sizeof(helloworld_valufact_statics));
+
+ _g_helloworld_valufact_statics->_etch_helloworld_valufact_typemap = new_vf_types_collection(ETCH_DEFVF_IDNMAP_DEFINITSIZE);
+ _g_helloworld_valufact_statics->_etch_helloworld_valufact_c2tmap = new_class_to_type_map(ETCH_DEVVF_C2TMAP_DEFINITSIZE);
+
+ defvf_initialize_static(_g_helloworld_valufact_statics->_etch_helloworld_valufact_typemap, _g_helloworld_valufact_statics->_etch_helloworld_valufact_c2tmap);
+ helloworld_valufact_init_static_types();
+ helloworld_valufact_init_static_fields();
+ helloworld_valufact_init_static_parameters();
+ helloworld_valufact_init_static_serializers();
+ etch_runtime_shutdown_hook_add(helloworld_etch_runtime_shutdown_hook_func);
+ }
+
+ return _g_helloworld_valufact_statics;
+}
+
+
+/**
+ * helloworld_valufact_init_types()
+ * instantiate type objects
+ */
+static int helloworld_valufact_init_static_types ()
+{
+ int restype = NULL;
+ struct i_hashtable* vtab = NULL;
+ helloworld_valufact_statics* p = helloworld_valufact_get_static();
+
+ /* instantiate type name strings */
+
+ p->str_helloworld_say_hello = new_wchar(L"etch.cbinding.test.helloworld.say_hello");
+ p->str_helloworld__result_say_hello = new_wchar(L"etch.cbinding.test.helloworld._result_say_hello");
+
+ p->str_helloworld_user = new_wchar(L"etch.cbinding.test.helloworld.user");
+ p->str_helloworld_UserUnknownException = new_wchar(L"etch.cbinding.test.helloworld.UserUnknownException");
+
+ /* instantiate type objects */
+ p->_mt_helloworld_say_hello = new_static_type(p->str_helloworld_say_hello);
+ ETCH_ASSERT(p->_mt_helloworld_say_hello);
+ p->_mt_helloworld__result_say_hello = new_static_type(p->str_helloworld__result_say_hello);
+ ETCH_ASSERT(p->_mt_helloworld__result_say_hello);
+
+ p->_mt_helloworld_user = new_static_type(p->str_helloworld_user);
+ ETCH_ASSERT(p->_mt_helloworld_user);
+ p->_mt_helloworld_UserUnknownException = new_static_type(p->str_helloworld_UserUnknownException);
+ ETCH_ASSERT(p->_mt_helloworld_UserUnknownException);
+ vtab = (struct i_hashtable*)((etch_object*)p->_etch_helloworld_valufact_typemap)->vtab;
+ /* add types to vf */
+ restype = vtab->inserth(p->_etch_helloworld_valufact_typemap->realtable, p->_mt_helloworld_say_hello, NULL, p->_etch_helloworld_valufact_typemap, 0);
+ ETCH_ASSERT(! restype);
+ restype = vtab->inserth(p->_etch_helloworld_valufact_typemap->realtable, p->_mt_helloworld__result_say_hello, NULL, p->_etch_helloworld_valufact_typemap, 0);
+ ETCH_ASSERT(! restype);
+
+ restype = vtab->inserth(p->_etch_helloworld_valufact_typemap->realtable, p->_mt_helloworld_user, NULL, p->_etch_helloworld_valufact_typemap, 0);
+ ETCH_ASSERT(! restype);
+ restype = vtab->inserth(p->_etch_helloworld_valufact_typemap->realtable, p->_mt_helloworld_UserUnknownException, NULL, p->_etch_helloworld_valufact_typemap, 0);
+ ETCH_ASSERT(! restype);
+
+ /* set type response fields */
+ etchtype_set_response_field(p->_mt_helloworld__result_say_hello, builtins._mf_result);
+
+ /* set message result types */
+ etchtype_set_result_type (p->_mt_helloworld_say_hello, p->_mt_helloworld__result_say_hello);
+
+ /* set timeouts */
+ etchtype_set_timeout (p->_mt_helloworld__result_say_hello, 0);
+
+ /* set async modes */
+ etchtype_set_async_mode(p->_mt_helloworld_say_hello, ETCH_ASYNCMODE_NONE);
+
+ return 0;
+}
+
+
+/**
+ * helloworld_valufact_init_fields()
+ * instantiate field objects
+ */
+static int helloworld_valufact_init_static_fields ()
+{
+ helloworld_valufact_get_static()->str_helloworld_id = new_wchar(L"id");
+ helloworld_valufact_get_static()->str_helloworld_name = new_wchar(L"name");
+ helloworld_valufact_get_static()->str_helloworld_mes = new_wchar(L"mes");
+ helloworld_valufact_get_static()->str_helloworld_to_whom = new_wchar(L"to_whom");
+
+ helloworld_valufact_get_static()->_mf_helloworld_id = new_static_field(helloworld_valufact_get_static()->str_helloworld_id);
+ helloworld_valufact_get_static()->_mf_helloworld_name = new_static_field(helloworld_valufact_get_static()->str_helloworld_name);
+ helloworld_valufact_get_static()->_mf_helloworld_mes = new_static_field(helloworld_valufact_get_static()->str_helloworld_mes);
+ helloworld_valufact_get_static()->_mf_helloworld_to_whom = new_static_field(helloworld_valufact_get_static()->str_helloworld_to_whom);
+ return 0;
+
+}
+
+/**
+ * helloworld_valufact_init_parameters()
+ * initialize service method parameters
+ */
+static int helloworld_valufact_init_static_parameters ()
+{
+ helloworld_valufact_statics* p = helloworld_valufact_get_static();
+
+
+//params for built-in List( etch_arraylist )
+//params for built-in Map( etch_hashtable )
+//params for built-in Set( etch_set )
+//params for built-in Datetime( etch_date )
+//params for struct user ([int id, string name])
+ etchtype_put_validator(p->_mt_helloworld_user,
+ clone_field(p->_mf_helloworld_id),
+ (etch_object*)etchvtor_int32_get( 0 ));
+ etchtype_put_validator(p->_mt_helloworld_user,
+ clone_field(p->_mf_helloworld_name),
+ (etch_object*)etchvtor_string_get( 0 ));
+//params for except UserUnknownException ([string mes])
+ etchtype_put_validator(p->_mt_helloworld_UserUnknownException,
+ clone_field(p->_mf_helloworld_mes),
+ (etch_object*)etchvtor_string_get( 0 ));
+//params for message SERVER string say_hello ([user to_whom]) throws [UserUnknownException]
+ etchtype_put_validator(p->_mt_helloworld_say_hello,
+ clone_field(p->_mf_helloworld_to_whom),
+ (etch_object*)etchvtor_custom_get(ETCHTYPEB_USER, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USER), helloworld_valufact_get_static()->_mt_helloworld_user, 0));
+ etchtype_put_validator(p->_mt_helloworld_say_hello,
+ clone_field(builtins._mf__message_id), (etch_object*) etchvtor_int64_get(0));
+//params for message CLIENT void _result_say_hello ([string result]) throws []
+ etchtype_put_validator(p->_mt_helloworld__result_say_hello,
+ builtins._mf_result,
+ (etch_object*)etchvtor_string_get( 0 ));
+ etchtype_put_validator(p->_mt_helloworld__result_say_hello,
+ clone_field(builtins._mf__message_id), (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(p->_mt_helloworld__result_say_hello,
+ clone_field(builtins._mf_result), (etch_object*) etchvtor_exception_get(0));
+ etchtype_put_validator(p->_mt_helloworld__result_say_hello,
+ clone_field(builtins._mf__in_reply_to), (etch_object*) etchvtor_int64_get(0));
+ etchtype_put_validator(p->_mt_helloworld__result_say_hello,
+ builtins._mf_result,
+ (etch_object*)etchvtor_custom_get( ETCHTYPEB_USER,get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USERUNKNOWNEXCEPTION), helloworld_valufact_get_static()->_mt_helloworld_UserUnknownException,0));
+ etchtype_put_validator(p->_mt_helloworld__result_say_hello,
+ builtins._mf_result,
+ (etch_object*)etchvtor_string_get( 0 ));
+
+
+//OLD
+//structs
+ return 0;
+}
+
+/**
+ * helloworld_valufact_init_serializers()
+ */
+static int helloworld_valufact_init_static_serializers ()
+{
+ int result = 0;
+ helloworld_valufact_statics* p = helloworld_valufact_get_static();
+ class_to_type_map* c2tmap = p->_etch_helloworld_valufact_c2tmap;
+
+ const unsigned short classid_helloworld_user = get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USER);
+ const unsigned short classid_helloworld_UserUnknownException = get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USERUNKNOWNEXCEPTION);
+
+ /* note that etch_serializer_init takes care of the class to type, setting
+ * of component type, instantiation of import export helper and installing
+ * it to the type.
+ */
+
+ /* serializer for helloworld_user */
+ result = etch_serializer_init (p->_mt_helloworld_user, p->str_helloworld_user,
+ ETCHMAKECLASS(ETCHTYPEB_USER, classid_helloworld_user),
+ c2tmap, NULL, new_helloworld_user_serializer);
+
+ /* serializer for helloworld_UserUnknownException */
+ result = etch_serializer_init (p->_mt_helloworld_UserUnknownException, p->str_helloworld_UserUnknownException,
+ ETCHMAKECLASS(ETCHTYPEB_EXCEPTION, classid_helloworld_UserUnknownException),
+ c2tmap, NULL, new_helloworld_UserUnknownException_serializer);
+
+ return result;
+}
+
+
+
+/**
+ * destroy_helloworld_valufact_impl()
+ * destructor for the helloworld value factory extension
+ */
+int destroy_helloworld_valufact_impl(void* data)
+{
+ helloworld_valufact_impl* impl = (helloworld_valufact_impl*)data;
+ if (NULL == impl) return -1;
+
+ if (!is_etchobj_static_content(impl))
+ {
+ // add if neccessary
+ }
+
+ return destroy_objectex((etch_object*) impl);
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * constructors/destructors
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+helloworld_valufact* new_helloworld_valufact()
+{
+ helloworld_valufact_impl* impl = NULL;
+ default_value_factory* pvf = new_default_value_factory (
+ helloworld_valufact_get_static()->_etch_helloworld_valufact_typemap,
+ helloworld_valufact_get_static()->_etch_helloworld_valufact_c2tmap);
+ ETCH_ASSERT(pvf);
+
+ /* note that the vf destructor is the default value factory destructor,
+ * which in turn invokes the destructor on its impl object
+ */
+ impl = (helloworld_valufact_impl*) new_object (sizeof(helloworld_valufact_impl),
+ ETCHTYPEB_VALUEFACTIMP, get_dynamic_classid_unique(&CLASSID_HELLOWORLD_VALUFACT_IMPL));
+
+ ((etch_object*)impl)->destroy = destroy_helloworld_valufact_impl;
+ pvf->impl = (etch_object*) impl;
+ return pvf;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * vf class methods
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+void helloworld_valufact_free_statics ()
+{
+
+ helloworld_valufact_statics* data = helloworld_valufact_get_static();
+ ETCH_ASSERT(data);
+
+ destroy_static_type (data->_mt_helloworld_say_hello);
+ destroy_static_type (data->_mt_helloworld__result_say_hello);
+
+ destroy_static_type (data->_mt_helloworld_user);
+ destroy_static_type (data->_mt_helloworld_UserUnknownException);
+
+ destroy_static_field(data->_mf_helloworld_id);
+ destroy_static_field(data->_mf_helloworld_name);
+ destroy_static_field(data->_mf_helloworld_mes);
+ destroy_static_field(data->_mf_helloworld_to_whom);
+
+ etch_free (data->str_helloworld_id);
+ etch_free (data->str_helloworld_name);
+ etch_free (data->str_helloworld_mes);
+ etch_free (data->str_helloworld_to_whom);
+
+ etch_free (data->str_helloworld_say_hello);
+ etch_free (data->str_helloworld__result_say_hello);
+
+ etch_free (data->str_helloworld_user);
+ etch_free (data->str_helloworld_UserUnknownException);
+
+
+ //set_etchobj_static_content(data->_etch_helloworld_valufact_typemap);
+ //set_etchobj_static_content(data->_etch_helloworld_valufact_c2tmap);
+
+ data->_etch_helloworld_valufact_typemap->is_readonly_keys = 0;
+ data->_etch_helloworld_valufact_c2tmap->is_readonly_keys = 0;
+ etch_object_destroy(data->_etch_helloworld_valufact_typemap);
+ etch_object_destroy(data->_etch_helloworld_valufact_c2tmap);
+
+ etch_free(data);
+ _g_helloworld_valufact_statics = NULL;
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - -
+ * serializers
+ * - - - - - - - - - - - - - - - - - - - -
+ */
+
+/**
+ * etchserializer_helloworld_user_export_value()
+ * export valueof a helloworld_user
+ * @param objval a helloworld_user, caller owns it and presumably will
+ * destroy it upon return from this method.
+ * @return the exported disposable structvalue object. caller must cast it.
+ */
+etch_object* etchserializer_helloworld_user_export_value(etch_serializer* thisx, etch_object* objval)
+{
+ const int THISINITSIZE = 2;
+ etch_structvalue* expstruct = NULL;
+ const unsigned short classid_helloworld_user = get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USER);
+ if (!is_etch_objparams(objval, ETCHTYPEB_USER, classid_helloworld_user)) return NULL;
+
+ expstruct = new_structvalue((etch_type*) thisx->type, THISINITSIZE);
+
+
+ structvalue_put(expstruct, _g_helloworld_valufact_statics->_mf_helloworld_id,
+ (etch_object*) new_int32(((helloworld_user*)objval)->id));
+ if(((helloworld_user*)objval)->name) {
+ structvalue_put(expstruct, _g_helloworld_valufact_statics->_mf_helloworld_name,
+ etch_object_clone_func((void*)((helloworld_user*)objval)->name));
+ }
+ return (etch_object*) expstruct; /* caller owns this structvalue */
+}
+
+/**
+ * etchserializer_helloworld_user_import_value()
+ * import value for a helloworld_user.
+ * @param objval an etch_structvalue of appropriate type for the helloworld_user.
+ * caller retains ownership of this object as with all imports.
+ * @return an opaque etch object containing the imported helloworld_user.
+ * caller owns and must destroy the returned object.
+ */
+etch_object* etchserializer_helloworld_user_import_value (etch_serializer* thisx, etch_object* objval)
+{
+ helloworld_user* outobj = NULL;
+
+ etch_int32* valobj_id = NULL;
+ etch_string* valobj_name = NULL;
+
+
+ etch_structvalue* instruct = NULL;
+ if (!is_etch_struct(objval)) return NULL;
+
+ instruct = (etch_structvalue*) objval;
+ if (!structvalue_is_type(instruct, thisx->type)) return NULL;
+
+
+ /* fetch the id value wrapper out of the struct. struct owns it */
+ valobj_id = (etch_int32*) structvalue_get(instruct, _g_helloworld_valufact_statics->_mf_helloworld_id);
+ if (valobj_id && !is_etch_int32(valobj_id)) return NULL;
+ /* fetch the name value wrapper out of the struct. struct owns it */
+ valobj_name = (etch_string*) structvalue_get(instruct, _g_helloworld_valufact_statics->_mf_helloworld_name);
+ if (valobj_name && !is_etch_string(valobj_name)) return NULL;
+
+ outobj = new_helloworld_user();
+
+
+ if(valobj_id)
+ outobj->id = valobj_id->value;
+ if(valobj_name)
+ outobj->name = (etch_string*) etch_object_clone_func((void*)valobj_name);
+ return (etch_object*) outobj; /* caller owns this object */
+}
+
+/**
+ * new_helloworld_user_serializer()
+ * etch_serializer_excp constructor - conforms to typedef etch_serializer_ctor
+ * @param type - not owned
+ * @param field - not owned
+ */
+etch_serializer* new_helloworld_user_serializer(etch_type* type, etch_field* field)
+{
+ etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);
+
+ ((etch_object*)newobj)->class_id = get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USER_SERIALIZER);
+ newobj->type = type; /* not owned */
+ newobj->field = field; /* not owned */
+
+ newobj->export_value = etchserializer_helloworld_user_export_value;
+ newobj->import_value = etchserializer_helloworld_user_import_value;
+
+ return newobj;
+}
+
+/**
+ * etchserializer_helloworld_UserUnknownException_export_value()
+ * export valueof a helloworld_UserUnknownException
+ * @param objval a helloworld_UserUnknownException, caller owns it and presumably will
+ * destroy it upon return from this method.
+ * @return the exported disposable structvalue object. caller must cast it.
+ */
+etch_object* etchserializer_helloworld_UserUnknownException_export_value(etch_serializer* thisx, etch_object* objval)
+{
+ const int THISINITSIZE = 2;
+ etch_structvalue* expstruct = NULL;
+ const unsigned short classid_helloworld_UserUnknownException = get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USERUNKNOWNEXCEPTION);
+ if (!is_etch_objparams(objval, ETCHTYPEB_EXCEPTION, classid_helloworld_UserUnknownException)) return NULL;
+
+ expstruct = new_structvalue((etch_type*) thisx->type, THISINITSIZE);
+
+
+ if(((helloworld_UserUnknownException*)objval)->mes) {
+ structvalue_put(expstruct, _g_helloworld_valufact_statics->_mf_helloworld_mes,
+ etch_object_clone_func((void*)((helloworld_UserUnknownException*)objval)->mes));
+ }
+ return (etch_object*) expstruct; /* caller owns this structvalue */
+}
+
+/**
+ * etchserializer_helloworld_UserUnknownException_import_value()
+ * import value for a helloworld_UserUnknownException.
+ * @param objval an etch_structvalue of appropriate type for the helloworld_UserUnknownException.
+ * caller retains ownership of this object as with all imports.
+ * @return an opaque etch object containing the imported helloworld_UserUnknownException.
+ * caller owns and must destroy the returned object.
+ */
+etch_object* etchserializer_helloworld_UserUnknownException_import_value (etch_serializer* thisx, etch_object* objval)
+{
+ helloworld_UserUnknownException* outobj = NULL;
+
+ etch_string* valobj_mes = NULL;
+
+
+ etch_structvalue* instruct = NULL;
+ if (!is_etch_struct(objval)) return NULL;
+
+ instruct = (etch_structvalue*) objval;
+ if (!structvalue_is_type(instruct, thisx->type)) return NULL;
+
+
+ /* fetch the mes value wrapper out of the struct. struct owns it */
+ valobj_mes = (etch_string*) structvalue_get(instruct, _g_helloworld_valufact_statics->_mf_helloworld_mes);
+ if (valobj_mes && !is_etch_string(valobj_mes)) return NULL;
+
+ outobj = new_helloworld_UserUnknownException();
+
+
+ if(valobj_mes)
+ outobj->mes = (etch_string*) etch_object_clone_func((void*)valobj_mes);
+ return (etch_object*) outobj; /* caller owns this object */
+}
+
+/**
+ * new_helloworld_UserUnknownException_serializer()
+ * etch_serializer_excp constructor - conforms to typedef etch_serializer_ctor
+ * @param type - not owned
+ * @param field - not owned
+ */
+etch_serializer* new_helloworld_UserUnknownException_serializer(etch_type* type, etch_field* field)
+{
+ etch_serializer* newobj = new_etch_serializer(ETCH_DEFSIZE);
+
+ ((etch_object*)newobj)->class_id = get_dynamic_classid_unique(&CLASSID_HELLOWORLD_USERUNKNOWNEXCEPTION_SERIALIZER);
+ newobj->type = type; /* not owned */
+ newobj->field = field; /* not owned */
+
+ newobj->export_value = etchserializer_helloworld_UserUnknownException_export_value;
+ newobj->import_value = etchserializer_helloworld_UserUnknownException_import_value;
+
+ return newobj;
+}
+
diff --git a/examples/helloworld/c/helloworld_valufact.h b/examples/helloworld/c/helloworld_valufact.h
new file mode 100644
index 0000000..feb9424
--- /dev/null
+++ b/examples/helloworld/c/helloworld_valufact.h
@@ -0,0 +1,104 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+
+/*
+ * helloworld_valufact.h
+ * helloworld implementation of value factory
+ */
+
+#ifndef HELLOWORLD_VALUFACT_H
+#define HELLOWORLD_VALUFACT_H
+
+#include "etch_default_value_factory.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef default_value_factory helloworld_valufact;
+
+extern unsigned short CLASSID_HELLOWORLD_VALUFACT_IMPL;
+
+unsigned short CLASSID_HELLOWORLD_USER_SERIALIZER;
+unsigned short CLASSID_HELLOWORLD_USERUNKNOWNEXCEPTION_SERIALIZER;
+
+
+/**
+ * helloworld_valufact_statics
+ */
+typedef struct helloworld_valufact_statics
+{
+ class_to_type_map* _etch_helloworld_valufact_c2tmap;
+ vf_idname_map* _etch_helloworld_valufact_typemap;
+
+ etch_type* _mt_helloworld_say_hello;
+ etch_type* _mt_helloworld__result_say_hello;
+
+ etch_type* _mt_helloworld_user;
+ etch_type* _mt_helloworld_UserUnknownException;
+
+
+ etch_field* _mf_helloworld_id;
+ etch_field* _mf_helloworld_name;
+ etch_field* _mf_helloworld_mes;
+ etch_field* _mf_helloworld_to_whom;
+
+ struct etch_serializer* serializer_user;
+ struct etch_serializer* serializer_UserUnknownException;
+
+ wchar_t* str_helloworld_id;
+ wchar_t* str_helloworld_name;
+ wchar_t* str_helloworld_mes;
+ wchar_t* str_helloworld_to_whom;
+
+ wchar_t* str_helloworld_say_hello;
+ wchar_t* str_helloworld__result_say_hello;
+ wchar_t* str_helloworld_user;
+ wchar_t* str_helloworld_UserUnknownException;
+
+} helloworld_valufact_statics;
+
+/**
+ * helloworld_valufact_impl
+ * helloworld extension of default value factory
+ */
+typedef struct helloworld_valufact_impl
+{
+ etch_object object;
+
+} helloworld_valufact_impl;
+
+helloworld_valufact* new_helloworld_valufact();
+helloworld_valufact_statics* helloworld_valufact_get_static();
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+#endif
+
diff --git a/examples/helloworld/c/readme-etch-c-files.txt b/examples/helloworld/c/readme-etch-c-files.txt
new file mode 100644
index 0000000..00ec1ed
--- /dev/null
+++ b/examples/helloworld/c/readme-etch-c-files.txt
@@ -0,0 +1,10 @@
+
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / c 1.1.0-incubating (LOCAL-0)
+// Fri Aug 28 15:58:20 CEST 2009
+// This file is automatically created and should not be edited!
+
+Generated C Binding files of Apache Etch.
+
+Add your specific implementations to xxx_client_impl.c/xxx_server_impl.c
+Startup is done via xxx_listener_main.c/xxx_client_main.c
diff --git a/examples/helloworld/etch/generate-c-and-java.bat b/examples/helloworld/etch/generate-c-and-java.bat
new file mode 100644
index 0000000..7dabc9b
--- /dev/null
+++ b/examples/helloworld/etch/generate-c-and-java.bat
@@ -0,0 +1,23 @@
+rem
+rem
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements. See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership. The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied. See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+rem
+rem
+call ..\..\..\target\Installers\dist\bin\etch.bat -b java -w BOTH,IMPL,INTF,MAIN -d ..\java helloworld.etch
+call ..\..\..\target\Installers\dist\bin\etch.bat -b c -w BOTH,IMPL,INTF,MAIN -d ..\c helloworld.etch
+
diff --git a/examples/helloworld/etch/helloworld.etch b/examples/helloworld/etch/helloworld.etch
new file mode 100644
index 0000000..66253e1
--- /dev/null
+++ b/examples/helloworld/etch/helloworld.etch
@@ -0,0 +1,16 @@
+module etch.cbinding.test
+
+service helloworld {
+ struct user (
+ int id,
+ string name
+ )
+
+ exception UserUnknownException (
+ string mes
+ )
+
+ @Direction(Server)
+ string say_hello(user to_whom) throws UserUnknownException
+
+}
\ No newline at end of file
diff --git a/examples/helloworld/java/.classpath b/examples/helloworld/java/.classpath
new file mode 100644
index 0000000..43330c1
--- /dev/null
+++ b/examples/helloworld/java/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path=""/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="lib" path="apache-etch-java-runtime-1.1.0-incubating.jar"/>
+ <classpathentry kind="output" path=""/>
+</classpath>
diff --git a/examples/helloworld/java/.project b/examples/helloworld/java/.project
new file mode 100644
index 0000000..020a918
--- /dev/null
+++ b/examples/helloworld/java/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>helloworld</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/examples/helloworld/java/apache-etch-java-runtime-1.1.0-incubating.jar b/examples/helloworld/java/apache-etch-java-runtime-1.1.0-incubating.jar
new file mode 100644
index 0000000..3c39188
--- /dev/null
+++ b/examples/helloworld/java/apache-etch-java-runtime-1.1.0-incubating.jar
Binary files differ
diff --git a/examples/helloworld/java/etch/cbinding/test/BasehelloworldClient.java b/examples/helloworld/java/etch/cbinding/test/BasehelloworldClient.java
new file mode 100644
index 0000000..ca4d5e1
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/BasehelloworldClient.java
@@ -0,0 +1,57 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+// Re-implement these methods by overriding them in ImplhelloworldClient.
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import org.apache.etch.bindings.java.support.ObjSession;
+
+
+/**
+ * Base implementation of helloworldClient, with default method implementations
+ * which throw UnsupportedOperationException. Extend this class to provide
+ * implementations of messages from the server.
+ *
+ * @see ImplhelloworldClient
+ */
+public class BasehelloworldClient implements helloworldClient, ObjSession
+{
+ public Object _sessionQuery( Object query ) throws Exception
+ {
+ throw new UnsupportedOperationException( "unknown query: "+query );
+ }
+
+ public void _sessionControl( Object control, Object value ) throws Exception
+ {
+ throw new UnsupportedOperationException( "unknown control: "+control );
+ }
+
+ public void _sessionNotify( Object event ) throws Exception
+ {
+ if (event instanceof Throwable)
+ ((Throwable) event).printStackTrace();
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/BasehelloworldServer.java b/examples/helloworld/java/etch/cbinding/test/BasehelloworldServer.java
new file mode 100644
index 0000000..3e4522e
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/BasehelloworldServer.java
@@ -0,0 +1,67 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+// Re-implement these methods by overriding them in ImplhelloworldServer.
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import org.apache.etch.bindings.java.support.ObjSession;
+
+
+/**
+ * Base implementation of helloworldServer, with default method implementations
+ * which throw UnsupportedOperationException. Extend this class to provide
+ * implementations of messages from the client.
+ *
+ * @see ImplhelloworldServer
+ */
+public class BasehelloworldServer implements helloworldServer, ObjSession
+{
+ public Object _sessionQuery( Object query ) throws Exception
+ {
+ throw new UnsupportedOperationException( "unknown query: "+query );
+ }
+
+ public void _sessionControl( Object control, Object value ) throws Exception
+ {
+ throw new UnsupportedOperationException( "unknown control: "+control );
+ }
+
+ public void _sessionNotify( Object event ) throws Exception
+ {
+ if (event instanceof Throwable)
+ ((Throwable) event).printStackTrace();
+ }
+
+ public String say_hello
+ (
+ etch.cbinding.test.helloworld.user to_whom
+ )
+ throws
+ etch.cbinding.test.helloworld.UserUnknownException
+ {
+ throw new UnsupportedOperationException( "say_hello" );
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/ImplhelloworldClient.java b/examples/helloworld/java/etch/cbinding/test/ImplhelloworldClient.java
new file mode 100644
index 0000000..b80533f
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/ImplhelloworldClient.java
@@ -0,0 +1,57 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Feb 05 15:40:43 CET 2010
+// This file is automatically created for your convenience and will not be
+// overwritten once it exists! Please edit this file as necessary to implement
+// your service logic.
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+/**
+ * Your custom implementation of BasehelloworldClient. Add methods here to provide
+ * implementations of messages from the server.
+ */
+public class ImplhelloworldClient extends BasehelloworldClient
+{
+ /**
+ * Constructs the ImplhelloworldClient.
+ *
+ * @param server a connection to the server session. Use this to send a
+ * message to the server.
+ */
+ public ImplhelloworldClient( RemotehelloworldServer server )
+ {
+ this.server = server;
+ }
+
+ /**
+ * A connection to the server session. Use this to send a
+ * message to the server.
+ */
+ @SuppressWarnings( "unused" )
+ private final RemotehelloworldServer server;
+
+ // TODO insert methods here to provide implementations of helloworldClient
+ // messages from the server.
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/ImplhelloworldServer.java b/examples/helloworld/java/etch/cbinding/test/ImplhelloworldServer.java
new file mode 100644
index 0000000..7ac5bfa
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/ImplhelloworldServer.java
@@ -0,0 +1,62 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Feb 05 15:40:43 CET 2010
+// This file is automatically created for your convenience and will not be
+// overwritten once it exists! Please edit this file as necessary to implement
+// your service logic.
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+/**
+ * Your custom implementation of BasehelloworldServer. Add methods here to provide
+ * implementations of messages from the client.
+ */
+public class ImplhelloworldServer extends BasehelloworldServer
+{
+ /**
+ * Constructs the ImplhelloworldServer.
+ *
+ * @param client a connection to the client session. Use this to send a
+ * message to the client.
+ */
+ public ImplhelloworldServer( RemotehelloworldClient client )
+ {
+ this.client = client;
+ }
+
+ /**
+ * A connection to the client session. Use this to send a
+ * message to the client.
+ */
+ @SuppressWarnings( "unused" )
+ private final RemotehelloworldClient client;
+
+ // TODO insert methods here to provide implementations of helloworldServer
+ // messages from the client.
+
+ @Override
+ public String say_hello(user toWhom) throws UserUnknownException {
+ return "Hello " + toWhom.name + ", nice to meet you!";
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/MainhelloworldClient.java b/examples/helloworld/java/etch/cbinding/test/MainhelloworldClient.java
new file mode 100644
index 0000000..c8eecce
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/MainhelloworldClient.java
@@ -0,0 +1,65 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Feb 05 15:40:43 CET 2010
+// This file is automatically created for your convenience and will not be
+// overwritten once it exists! Please edit this file as necessary to implement
+// your service logic.
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+/**
+ * Main program for helloworldClient. This program makes a connection to the
+ * listener created by MainhelloworldListener.
+ */
+public class MainhelloworldClient implements helloworldHelper.helloworldClientFactory
+{
+ /**
+ * Main program for helloworldClient.
+ *
+ * @param args command line arguments.
+ * @throws Exception
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ // TODO Change to correct URI
+ String uri = "tcp://localhost:4001";
+
+ RemotehelloworldServer server = helloworldHelper.newServer( uri, null,
+ new MainhelloworldClient() );
+
+ // Connect to the service
+ server._startAndWaitUp( 4000 );
+
+ // TODO Insert Your Code Here
+
+ // Disconnect from the service
+ server._stopAndWaitDown( 4000 );
+ }
+
+ public helloworldClient newhelloworldClient( RemotehelloworldServer server )
+ throws Exception
+ {
+ return new ImplhelloworldClient( server );
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/MainhelloworldListener.java b/examples/helloworld/java/etch/cbinding/test/MainhelloworldListener.java
new file mode 100644
index 0000000..30d1d8c
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/MainhelloworldListener.java
@@ -0,0 +1,63 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Feb 05 15:40:43 CET 2010
+// This file is automatically created for your convenience and will not be
+// overwritten once it exists! Please edit this file as necessary to implement
+// your service logic.
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import org.apache.etch.bindings.java.support.ServerFactory;
+import org.apache.etch.util.core.io.Transport;
+
+/**
+ * Main program for helloworldServer. This program makes a listener to accept
+ * connections from MainhelloworldClient.
+ */
+public class MainhelloworldListener implements helloworldHelper.helloworldServerFactory
+{
+ /**
+ * Main program for helloworldServer.
+ *
+ * @param args command line arguments.
+ * @throws Exception
+ */
+ public static void main( String[] args ) throws Exception
+ {
+ // TODO Change to correct URI
+ String uri = "tcp://0.0.0.0:4004";
+
+ ServerFactory listener = helloworldHelper.newListener( uri, null,
+ new MainhelloworldListener() );
+ System.out.println("This is the hello server...");
+ // Start the Listener
+ listener.transportControl( Transport.START_AND_WAIT_UP, 9999999 );
+
+ }
+
+ public helloworldServer newhelloworldServer( RemotehelloworldClient client )
+ {
+ return new ImplhelloworldServer( client );
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/Remotehelloworld.java b/examples/helloworld/java/etch/cbinding/test/Remotehelloworld.java
new file mode 100644
index 0000000..eb3c1dd
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/Remotehelloworld.java
@@ -0,0 +1,71 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+
+/**
+ * Call to message translator for helloworld.
+ */
+@SuppressWarnings("unused")
+public class Remotehelloworld extends org.apache.etch.bindings.java.support.RemoteBase implements helloworld
+{
+ /**
+ * Constructs the Remotehelloworld.
+ *
+ * @param svc
+ * @param vf
+ */
+ public Remotehelloworld( org.apache.etch.bindings.java.support.DeliveryService svc, org.apache.etch.bindings.java.msg.ValueFactory vf )
+ {
+ super( svc, vf );
+ }
+
+ /**
+ * {@link _Async} class instance used to hide asynchronous message
+ * implementation. Use this to invoke the asynchronous message
+ * implementations.
+ */
+ public final _Async _async = new _Async();
+
+ /**
+ * {@link _Async} class instance used to hide asynchronous message
+ * implementation. This is here for backwards compatibility only, use
+ * {@link #_async} instead.
+ * @deprecated
+ */
+ @Deprecated
+ public final _Async _inner = _async;
+
+ /**
+ * Asynchronous implementation of service methods.
+ */
+ public class _Async
+ {
+
+ // Mixin Methods
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/RemotehelloworldClient.java b/examples/helloworld/java/etch/cbinding/test/RemotehelloworldClient.java
new file mode 100644
index 0000000..6959eba
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/RemotehelloworldClient.java
@@ -0,0 +1,71 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+
+/**
+ * Call to message translator for helloworldClient.
+ */
+@SuppressWarnings("unused")
+public final class RemotehelloworldClient extends Remotehelloworld implements helloworldClient
+{
+ /**
+ * Constructs the RemotehelloworldClient.
+ *
+ * @param svc
+ * @param vf
+ */
+ public RemotehelloworldClient( org.apache.etch.bindings.java.support.DeliveryService svc, org.apache.etch.bindings.java.msg.ValueFactory vf )
+ {
+ super( svc, vf );
+ }
+
+ /**
+ * {@link _Async} class instance used to hide asynchronous message
+ * implementation. Use this to invoke the asynchronous message
+ * implementations.
+ */
+ public final _Async _async = new _Async();
+
+ /**
+ * {@link _Async} class instance used to hide asynchronous message
+ * implementation. This is here for backwards compatibility only, use
+ * {@link #_async} instead.
+ * @deprecated
+ */
+ @Deprecated
+ public final _Async _inner = _async;
+
+ /**
+ * Asynchronous implementation of service methods.
+ */
+ public final class _Async extends Remotehelloworld._Async
+ {
+
+ // Mixin Methods
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/RemotehelloworldServer.java b/examples/helloworld/java/etch/cbinding/test/RemotehelloworldServer.java
new file mode 100644
index 0000000..d91a504
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/RemotehelloworldServer.java
@@ -0,0 +1,127 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+
+/**
+ * Call to message translator for helloworldServer.
+ */
+@SuppressWarnings("unused")
+public final class RemotehelloworldServer extends Remotehelloworld implements helloworldServer
+{
+ /**
+ * Constructs the RemotehelloworldServer.
+ *
+ * @param svc
+ * @param vf
+ */
+ public RemotehelloworldServer( org.apache.etch.bindings.java.support.DeliveryService svc, org.apache.etch.bindings.java.msg.ValueFactory vf )
+ {
+ super( svc, vf );
+ }
+
+ /**
+ * {@link _Async} class instance used to hide asynchronous message
+ * implementation. Use this to invoke the asynchronous message
+ * implementations.
+ */
+ public final _Async _async = new _Async();
+
+ /**
+ * {@link _Async} class instance used to hide asynchronous message
+ * implementation. This is here for backwards compatibility only, use
+ * {@link #_async} instead.
+ * @deprecated
+ */
+ @Deprecated
+ public final _Async _inner = _async;
+
+ public final String say_hello(
+ etch.cbinding.test.helloworld.user to_whom
+ )
+ throws
+ etch.cbinding.test.helloworld.UserUnknownException
+ {
+ return
+ _async._end_say_hello( _async._begin_say_hello(
+ to_whom
+ ) );
+ }
+
+ /**
+ * Asynchronous implementation of service methods.
+ */
+ public final class _Async extends Remotehelloworld._Async
+ {
+
+ /**
+ * Begins a call to say_hello.
+ *
+ * @return mailbox used to retrieve the result using _end_say_hello.
+ * @see RemotehelloworldServer#say_hello
+ * @see #_end_say_hello
+ */
+ public final org.apache.etch.bindings.java.support.Mailbox _begin_say_hello(
+ etch.cbinding.test.helloworld.user to_whom
+ )
+ {
+ org.apache.etch.bindings.java.msg.Message _msg = _newMessage( ValueFactoryhelloworld._mt_etch_cbinding_test_helloworld_say_hello );
+ _msg.put( ValueFactoryhelloworld._mf_to_whom, to_whom );
+ return _begincall( _msg );
+ }
+
+ /**
+ * Ends a call to say_hello.
+ *
+ * @param mb mailbox returned by _begin_say_hello.
+ *
+ * @see RemotehelloworldServer#say_hello
+ * @see #_begin_say_hello
+ */
+ public final String _end_say_hello( org.apache.etch.bindings.java.support.Mailbox mb )
+ throws
+ etch.cbinding.test.helloworld.UserUnknownException
+ {
+ try
+ {
+ return
+ (String)
+ _endcall( mb,
+ ValueFactoryhelloworld._mt_etch_cbinding_test_helloworld__result_say_hello );
+ }
+ catch ( Exception e )
+ {
+ if (e instanceof etch.cbinding.test.helloworld.UserUnknownException)
+ throw (etch.cbinding.test.helloworld.UserUnknownException) e;
+ if (e instanceof RuntimeException) throw (RuntimeException) e;
+ throw new RuntimeException( "unexpected exception from peer: "+e, e );
+ }
+ }
+
+ // Mixin Methods
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/Stubhelloworld.java b/examples/helloworld/java/etch/cbinding/test/Stubhelloworld.java
new file mode 100644
index 0000000..28c6ac0
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/Stubhelloworld.java
@@ -0,0 +1,72 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import org.apache.etch.util.core.Who;
+import org.apache.etch.bindings.java.msg.Message;
+import org.apache.etch.bindings.java.support.DeliveryService;
+import org.apache.etch.bindings.java.support.Pool;
+import org.apache.etch.bindings.java.support.StubHelper;
+import org.apache.etch.bindings.java.support._Etch_AuthException;
+import org.apache.etch.bindings.java.support.StubBase;
+import org.apache.etch.bindings.java.support.Pool.PoolRunnable;
+
+/**
+ * Message to call translator for helloworld.
+ * @param <T> helloworld or a subclass thereof.
+ */
+@SuppressWarnings("unused")
+public class Stubhelloworld<T extends helloworld> extends StubBase<T>
+{
+ /**
+ * Stub for helloworld.
+ * @param svc the delivery service.
+ * @param obj the implementation of helloworld responsive to requests.
+ * @param queued thread pool used to run AsyncMode.QUEUED methods.
+ * @param free thread pool used to run AsyncMode.FREE methods.
+ */
+ public Stubhelloworld( DeliveryService svc, T obj, Pool queued, Pool free )
+ {
+ super( svc, obj, queued, free );
+ }
+
+ static
+ {
+ }
+
+ /**
+ * Method used to force static initialization.
+ */
+ public static void init()
+ {
+ // nothing to do.
+ }
+
+ static
+ {
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/StubhelloworldClient.java b/examples/helloworld/java/etch/cbinding/test/StubhelloworldClient.java
new file mode 100644
index 0000000..99b401f
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/StubhelloworldClient.java
@@ -0,0 +1,70 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import org.apache.etch.util.core.Who;
+import org.apache.etch.bindings.java.msg.Message;
+import org.apache.etch.bindings.java.support.DeliveryService;
+import org.apache.etch.bindings.java.support.Pool;
+import org.apache.etch.bindings.java.support.StubHelper;
+import org.apache.etch.bindings.java.support._Etch_AuthException;
+import org.apache.etch.bindings.java.support.Pool.PoolRunnable;
+
+/**
+ * Message to call translator for helloworldClient.
+ */
+@SuppressWarnings("unused")
+public class StubhelloworldClient extends Stubhelloworld<helloworldClient>
+{
+ /**
+ * Stub for helloworldClient.
+ * @param svc the delivery service.
+ * @param obj the implementation of helloworldClient responsive to requests.
+ * @param queued thread pool used to run AsyncMode.QUEUED methods.
+ * @param free thread pool used to run AsyncMode.FREE methods.
+ */
+ public StubhelloworldClient( DeliveryService svc, helloworldClient obj, Pool queued, Pool free )
+ {
+ super( svc, obj, queued, free );
+ }
+
+ static
+ {
+ }
+
+ /**
+ * Method used to force static initialization.
+ */
+ public static void init()
+ {
+ // nothing to do.
+ }
+
+ static
+ {
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/StubhelloworldServer.java b/examples/helloworld/java/etch/cbinding/test/StubhelloworldServer.java
new file mode 100644
index 0000000..f3ee95f
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/StubhelloworldServer.java
@@ -0,0 +1,91 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import org.apache.etch.util.core.Who;
+import org.apache.etch.bindings.java.msg.Message;
+import org.apache.etch.bindings.java.support.DeliveryService;
+import org.apache.etch.bindings.java.support.Pool;
+import org.apache.etch.bindings.java.support.StubHelper;
+import org.apache.etch.bindings.java.support._Etch_AuthException;
+import org.apache.etch.bindings.java.support.Pool.PoolRunnable;
+
+/**
+ * Message to call translator for helloworldServer.
+ */
+@SuppressWarnings("unused")
+public class StubhelloworldServer extends Stubhelloworld<helloworldServer>
+{
+ /**
+ * Stub for helloworldServer.
+ * @param svc the delivery service.
+ * @param obj the implementation of helloworldServer responsive to requests.
+ * @param queued thread pool used to run AsyncMode.QUEUED methods.
+ * @param free thread pool used to run AsyncMode.FREE methods.
+ */
+ public StubhelloworldServer( DeliveryService svc, helloworldServer obj, Pool queued, Pool free )
+ {
+ super( svc, obj, queued, free );
+ }
+
+ static
+ {
+ }
+
+ /**
+ * Method used to force static initialization.
+ */
+ public static void init()
+ {
+ // nothing to do.
+ }
+
+ static
+ {
+ ValueFactoryhelloworld._mt_etch_cbinding_test_helloworld_say_hello.setStubHelper( new StubHelper<helloworldServer>()
+ {
+ public final void run( DeliveryService _svc, helloworldServer _obj, Who _sender, Message _msg ) throws Exception
+ {
+ final Message _rmsg = _msg.reply();
+ try
+ {
+ _rmsg.put( ValueFactoryhelloworld._mf_result,
+ _obj.say_hello(
+ (etch.cbinding.test.helloworld.user) _msg.get( ValueFactoryhelloworld._mf_to_whom )
+ )
+ );
+ }
+ catch ( Exception e )
+ {
+ sessionNotify( _obj, e );
+ _rmsg.put( ValueFactoryhelloworld._mf_result, e );
+ }
+ _svc.transportMessage( _sender, _rmsg );
+ }
+ } );
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/ValueFactoryhelloworld.java b/examples/helloworld/java/etch/cbinding/test/ValueFactoryhelloworld.java
new file mode 100644
index 0000000..282fa9c
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/ValueFactoryhelloworld.java
@@ -0,0 +1,203 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import org.apache.etch.bindings.java.msg.AsyncMode;
+import org.apache.etch.bindings.java.msg.Direction;
+import org.apache.etch.bindings.java.msg.Field;
+import org.apache.etch.bindings.java.msg.ImportExportHelper;
+import org.apache.etch.bindings.java.msg.StructValue;
+import org.apache.etch.bindings.java.msg.Type;
+import org.apache.etch.bindings.java.msg.TypeMap;
+import org.apache.etch.bindings.java.msg.ValueFactory;
+import org.apache.etch.bindings.java.support.Class2TypeMap;
+import org.apache.etch.bindings.java.support.DefaultValueFactory;
+
+
+/**
+ * ValueFactory for helloworld.
+ */
+@SuppressWarnings("unused")
+public final class ValueFactoryhelloworld extends DefaultValueFactory
+{
+ /**
+ * Constructs ValueFactoryhelloworld.
+ * @param uri the uri used to configure the session.
+ */
+ public ValueFactoryhelloworld( String uri )
+ {
+ super( uri, types, class2type );
+ }
+
+ private final static TypeMap types = new TypeMap();
+
+ private final static Class2TypeMap class2type = new Class2TypeMap();
+
+ static
+ {
+ DefaultValueFactory.init( types, class2type );
+ initTypes();
+ initResults();
+ initFields();
+ initParams();
+ }
+
+ /** Type for etch.cbinding.test.helloworld.user */
+ public static Type _mt_etch_cbinding_test_helloworld_user;
+
+ /** Type for etch.cbinding.test.helloworld.UserUnknownException */
+ public static Type _mt_etch_cbinding_test_helloworld_UserUnknownException;
+
+ /** Type for etch.cbinding.test.helloworld.say_hello */
+ public static Type _mt_etch_cbinding_test_helloworld_say_hello;
+
+ /** Type for etch.cbinding.test.helloworld._result_say_hello */
+ public static Type _mt_etch_cbinding_test_helloworld__result_say_hello;
+
+ private static void initTypes()
+ {
+ _mt_etch_cbinding_test_helloworld_user = types.get( "etch.cbinding.test.helloworld.user" );
+ _mt_etch_cbinding_test_helloworld_UserUnknownException = types.get( "etch.cbinding.test.helloworld.UserUnknownException" );
+ _mt_etch_cbinding_test_helloworld_say_hello = types.get( "etch.cbinding.test.helloworld.say_hello" );
+ _mt_etch_cbinding_test_helloworld__result_say_hello = types.get( "etch.cbinding.test.helloworld._result_say_hello" );
+ }
+
+ private static void initResults()
+ {
+ _mt_etch_cbinding_test_helloworld_say_hello.setDirection( Direction.SERVER );
+ _mt_etch_cbinding_test_helloworld_say_hello.setAsyncMode( AsyncMode.NONE );
+ _mt_etch_cbinding_test_helloworld_say_hello.setResult( _mt_etch_cbinding_test_helloworld__result_say_hello );
+ _mt_etch_cbinding_test_helloworld__result_say_hello.setTimeout( 0 );
+ _mt_etch_cbinding_test_helloworld__result_say_hello.setDirection( Direction.CLIENT );
+ }
+
+ static
+ {
+ class2type.put( etch.cbinding.test.helloworld.user.class, _mt_etch_cbinding_test_helloworld_user );
+ _mt_etch_cbinding_test_helloworld_user.setComponentType( etch.cbinding.test.helloworld.user.class );
+ _mt_etch_cbinding_test_helloworld_user.setImportExportHelper( new ImportExportHelper()
+ {
+ public final StructValue exportValue( ValueFactory vf, Object value )
+ {
+ StructValue struct = new StructValue( _mt_etch_cbinding_test_helloworld_user, vf );
+ etch.cbinding.test.helloworld.user v = (etch.cbinding.test.helloworld.user) value;
+ struct.put( _mf_id, v.id );
+ struct.put( _mf_name, v.name );
+ return struct;
+ }
+
+ public final Object importValue( StructValue struct )
+ {
+ etch.cbinding.test.helloworld.user v = new etch.cbinding.test.helloworld.user();
+ v.id = (Integer) struct.get( _mf_id );
+ v.name = (String) struct.get( _mf_name );
+ return v;
+ }
+ } );
+ }
+
+ static
+ {
+ class2type.put( etch.cbinding.test.helloworld.UserUnknownException.class, _mt_etch_cbinding_test_helloworld_UserUnknownException );
+ _mt_etch_cbinding_test_helloworld_UserUnknownException.setComponentType( etch.cbinding.test.helloworld.UserUnknownException.class );
+ _mt_etch_cbinding_test_helloworld_UserUnknownException.setImportExportHelper( new ImportExportHelper()
+ {
+ public final StructValue exportValue( ValueFactory vf, Object value )
+ {
+ StructValue struct = new StructValue( _mt_etch_cbinding_test_helloworld_UserUnknownException, vf );
+ etch.cbinding.test.helloworld.UserUnknownException v = (etch.cbinding.test.helloworld.UserUnknownException) value;
+ struct.put( _mf_mes, v.mes );
+ return struct;
+ }
+
+ public final Object importValue( StructValue struct )
+ {
+ etch.cbinding.test.helloworld.UserUnknownException v = new etch.cbinding.test.helloworld.UserUnknownException();
+ v.mes = (String) struct.get( _mf_mes );
+ return v;
+ }
+ } );
+ }
+
+ /** Field for id */
+ public static Field _mf_id;
+
+ /** Field for name */
+ public static Field _mf_name;
+
+ /** Field for mes */
+ public static Field _mf_mes;
+
+ /** Field for to_whom */
+ public static Field _mf_to_whom;
+
+ private static void initFields()
+ {
+ _mf_id = new Field( "id" );
+ _mf_name = new Field( "name" );
+ _mf_mes = new Field( "mes" );
+ _mf_to_whom = new Field( "to_whom" );
+
+ _mt_etch_cbinding_test_helloworld__result_say_hello.setResponseField( _mf_result );
+ }
+
+ static
+ {
+ // initialize the extern serializers:
+ }
+
+ private static void initParams()
+ {
+
+ // params for user
+ _mt_etch_cbinding_test_helloworld_user.putValidator( _mf_id, org.apache.etch.bindings.java.support.Validator_int.get( 0 ) );
+ _mt_etch_cbinding_test_helloworld_user.putValidator( _mf_name, org.apache.etch.bindings.java.support.Validator_string.get( 0 ) );
+
+ // params for UserUnknownException
+ _mt_etch_cbinding_test_helloworld_UserUnknownException.putValidator( _mf_mes, org.apache.etch.bindings.java.support.Validator_string.get( 0 ) );
+
+ // params for say_hello
+ _mt_etch_cbinding_test_helloworld_say_hello.putValidator( _mf_to_whom, org.apache.etch.bindings.java.support.Validator_custom.get( etch.cbinding.test.helloworld.user.class, 0, true ) );
+ _mt_etch_cbinding_test_helloworld_say_hello.putValidator( _mf__messageId, org.apache.etch.bindings.java.support.Validator_long.get( 0 ) );
+
+ // params for _result_say_hello
+ _mt_etch_cbinding_test_helloworld__result_say_hello.putValidator( _mf_result, org.apache.etch.bindings.java.support.Validator_string.get( 0 ) );
+ _mt_etch_cbinding_test_helloworld__result_say_hello.putValidator( _mf__messageId, org.apache.etch.bindings.java.support.Validator_long.get( 0 ) );
+ _mt_etch_cbinding_test_helloworld__result_say_hello.putValidator( _mf_result, org.apache.etch.bindings.java.support.Validator_custom.get( etch.cbinding.test.helloworld.UserUnknownException.class, 0, true ) ); // thrown UserUnknownException
+ _mt_etch_cbinding_test_helloworld__result_say_hello.putValidator( _mf_result, org.apache.etch.bindings.java.support.Validator_RuntimeException.get() ); // thrown RuntimeException
+ _mt_etch_cbinding_test_helloworld__result_say_hello.putValidator( _mf__inReplyTo, org.apache.etch.bindings.java.support.Validator_long.get( 0 ) );
+ }
+
+ static
+ {
+ // done updating types, and class2type: lock them.
+ types.lock();
+ for (Type t: types.values())
+ t.lock();
+ class2type.lock();
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/helloworld.java b/examples/helloworld/java/etch/cbinding/test/helloworld.java
new file mode 100644
index 0000000..c4cc057
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/helloworld.java
@@ -0,0 +1,169 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import java.io.Serializable;
+
+
+@SuppressWarnings("unused")
+public interface helloworld
+{
+ @SuppressWarnings("serial")
+ public class user
+ implements Serializable
+ {
+ /**
+ * Constructs the user. Don't init any fields.
+ */
+ public user()
+ {
+ // don't init any fields.
+ }
+
+ /**
+ * Constructs the user.
+ */
+ public user
+ (
+ Integer id
+ , String name
+ )
+ {
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format( "user(id=%s; name=%s)", id, name );
+ }
+
+ public Integer id;
+
+ /**
+ * Gets the value.
+ *
+ *
+ * @return the value.
+ */
+ public Integer getId()
+ {
+ return id;
+ }
+
+ /**
+ * Sets the value.
+ *
+ *
+ * @param value the value.
+ */
+ public void setId( Integer value )
+ {
+ this.id = value;
+ }
+
+ public String name;
+
+ /**
+ * Gets the value.
+ *
+ *
+ * @return the value.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Sets the value.
+ *
+ *
+ * @param value the value.
+ */
+ public void setName( String value )
+ {
+ this.name = value;
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public class UserUnknownException
+ extends Exception
+ {
+ /**
+ * Constructs the UserUnknownException. Don't init any fields.
+ */
+ public UserUnknownException()
+ {
+ // don't init any fields.
+ }
+
+ /**
+ * Constructs the UserUnknownException.
+ */
+ public UserUnknownException
+ (
+ String mes
+ )
+ {
+ this.mes = mes;
+ }
+
+ @Override
+ public String getMessage()
+ {
+ return String.format( "mes=%s", mes );
+ }
+
+ public String mes;
+
+ /**
+ * Gets the value.
+ *
+ *
+ * @return the value.
+ */
+ public String getMes()
+ {
+ return mes;
+ }
+
+ /**
+ * Sets the value.
+ *
+ *
+ * @param value the value.
+ */
+ public void setMes( String value )
+ {
+ this.mes = value;
+ }
+ }
+
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/helloworldClient.java b/examples/helloworld/java/etch/cbinding/test/helloworldClient.java
new file mode 100644
index 0000000..4fc7066
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/helloworldClient.java
@@ -0,0 +1,35 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+
+
+@SuppressWarnings("unused")
+public interface helloworldClient extends helloworld
+{
+ // no CLIENT direction items defined.
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/helloworldHelper.java b/examples/helloworld/java/etch/cbinding/test/helloworldHelper.java
new file mode 100644
index 0000000..7dd8b0a
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/helloworldHelper.java
@@ -0,0 +1,190 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+import org.apache.etch.bindings.java.support.DeliveryService;
+import org.apache.etch.bindings.java.support.Pool;
+import org.apache.etch.bindings.java.support.ServerFactory;
+import org.apache.etch.bindings.java.msg.ValueFactory;
+import org.apache.etch.bindings.java.support.DefaultServerFactory;
+import org.apache.etch.util.core.io.Transport;
+import org.apache.etch.bindings.java.support.TransportFactory;
+import org.apache.etch.bindings.java.support.TransportHelper;
+import org.apache.etch.bindings.java.transport.DefaultDeliveryService;
+import org.apache.etch.bindings.java.transport.MailboxManager;
+import org.apache.etch.bindings.java.transport.PlainMailboxManager;
+import org.apache.etch.bindings.java.transport.TransportMessage;
+import org.apache.etch.util.Resources;
+import org.apache.etch.util.URL;
+
+/**
+ * Transport helper for helloworld. All methods are static.
+ */
+abstract public class helloworldHelper extends TransportHelper
+{
+
+ /**
+ * Constructs a new server session listener per specifications in uri and
+ * resources. This listener will accept requests from clients for new server
+ * sessions.
+ *
+ * @param uri contains specifications for the server session listener and
+ * for the server session transport stack.
+ *
+ * @param resources additional resources to aid in constructing new server
+ * sessions.
+ *
+ * @param implFactory factory used to construct a new instance implementing
+ * helloworldServer. The new instance will receive and process messages from
+ * the client session.
+ *
+ * @return a server session listener.
+ *
+ * @throws Exception
+ */
+ public static ServerFactory newListener( final String uri,
+ final Resources resources, final helloworldServerFactory implFactory )
+ throws Exception
+ {
+ final Resources res = initResources( resources );
+
+ final Transport<ServerFactory> listener = TransportFactory.getListener( uri, res );
+
+ return new DefaultServerFactory( listener, implFactory )
+ {
+ public void newServer( TransportMessage t, String uri, Resources r )
+ throws Exception
+ {
+ ValueFactory vf = (ValueFactory) r.get( Transport.VALUE_FACTORY );
+ MailboxManager x = new PlainMailboxManager( t, uri, r );
+ DeliveryService d = new DefaultDeliveryService( x, uri, r );
+ RemotehelloworldClient client = new RemotehelloworldClient( d, vf );
+ helloworldServer server = implFactory.newhelloworldServer( client );
+ Pool qp = (Pool) r.get( QUEUED_POOL );
+ Pool fp = (Pool) r.get( FREE_POOL );
+ new StubhelloworldServer( d, server, qp, fp );
+ client._start();
+ }
+
+ public ValueFactory newValueFactory( String uri )
+ {
+ return new ValueFactoryhelloworld( uri );
+ }
+
+ @Override
+ public String toString()
+ {
+ return "helloworldHelper.ServerFactory/" + listener;
+ }
+ };
+ }
+
+ /**
+ * Factory used by
+ * {@link helloworldHelper#newListener(String, Resources, helloworldServerFactory)}
+ * to construct a new instance implementing {@link helloworldServer}. The new
+ * instance will receive and process messages from the client session.
+ */
+ public interface helloworldServerFactory
+ {
+ /**
+ * Constructs a new instance implementing helloworldServer. The new
+ * instance will receive and process messages from the client session.
+ *
+ * @param client an instance of RemotehelloworldClient which may be used to
+ * send messages to the client session.
+ * @return a new instance implementing helloworldServer (typically
+ * ImplhelloworldServer).
+ * @throws Exception
+ */
+ public helloworldServer newhelloworldServer( RemotehelloworldClient client )
+ throws Exception;
+ }
+
+ /**
+ * Constructs a new client session per specifications in uri and resources.
+ *
+ * @param uri contains specifications for the client session transport
+ * stack.
+ *
+ * @param resources additional resources to aid in constructing new client
+ * sessions.
+ *
+ * @param implFactory factory used to construct a new instance implementing
+ * helloworldClient. The new instance will receive and process messages from
+ * the server session.
+ *
+ * @return an instance of RemotehelloworldServer initialized by uri and
+ * resources which may be used to send messages to the server session.
+ *
+ * @throws Exception
+ */
+ public static RemotehelloworldServer newServer( String uri,
+ Resources resources, helloworldClientFactory implFactory )
+ throws Exception
+ {
+ final Resources res = initResources( resources );
+
+ final ValueFactoryhelloworld vf = new ValueFactoryhelloworld( uri );
+ res.put( Transport.VALUE_FACTORY, vf );
+
+ URL u = new URL( uri );
+
+ TransportMessage m = TransportFactory.getTransport( uri, res );
+ MailboxManager r = new PlainMailboxManager( m, u, resources );
+ DeliveryService d = new DefaultDeliveryService( r, u, resources );
+ RemotehelloworldServer server = new RemotehelloworldServer( d, vf );
+ helloworldClient client = implFactory.newhelloworldClient( server );
+ Pool qp = (Pool) res.get( QUEUED_POOL );
+ Pool fp = (Pool) res.get( FREE_POOL );
+ new StubhelloworldClient( d, client, qp, fp );
+
+ return server;
+ }
+
+ /**
+ * Factory used by
+ * {@link helloworldHelper#newServer(String, Resources, helloworldClientFactory)}
+ * to construct a new instance implementing {@link helloworldClient}. The new
+ * instance will receive and process messages from the server session.
+ */
+ public interface helloworldClientFactory
+ {
+ /**
+ * Constructs a new instance implementing helloworldClient. The new
+ * instance will receive and process messages from the server session.
+ *
+ * @param server an instance of RemotehelloworldServer which may be used to
+ * send messages to the server session.
+ * @return a new instance implementing helloworldClient (typically
+ * ImplhelloworldClient).
+ * @throws Exception
+ */
+ public helloworldClient newhelloworldClient( RemotehelloworldServer server )
+ throws Exception;
+ }
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/helloworldServer.java b/examples/helloworld/java/etch/cbinding/test/helloworldServer.java
new file mode 100644
index 0000000..31e4a1c
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/helloworldServer.java
@@ -0,0 +1,42 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+package etch.cbinding.test;
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+
+
+@SuppressWarnings("unused")
+public interface helloworldServer extends helloworld
+{
+ /**
+ */
+ public String say_hello(
+ etch.cbinding.test.helloworld.user to_whom
+ )
+ throws
+ etch.cbinding.test.helloworld.UserUnknownException
+ ;
+}
diff --git a/examples/helloworld/java/etch/cbinding/test/readme-etch-java-files.txt b/examples/helloworld/java/etch/cbinding/test/readme-etch-java-files.txt
new file mode 100644
index 0000000..2d02223
--- /dev/null
+++ b/examples/helloworld/java/etch/cbinding/test/readme-etch-java-files.txt
@@ -0,0 +1,122 @@
+// This file automatically generated by:
+// Apache Etch 1.1.0-incubating (LOCAL-0) / java 1.1.0-incubating (LOCAL-0)
+// Fri Jul 16 12:01:27 CEST 2010
+// This file is automatically created and should not be edited!
+
+This is a description of the etch-generated files for your service.
+
+----------------
+- How To Build -
+----------------
+
+In the directory where the etch file is located, execute the following command:
+
+etch -q -d . -I . -b java -w all Blah.etch
+
+This would compile the service description Blah.etch, generating all java files,
+into java packages rooted in the current directory (-d .), resolving includes
+and mixins from the current directory (-I .) and being quiet about it (-q).
+
+Assuming the Blah.etch specified a module of "demo" and a service of Blah,
+the java files would be generated into a sub-directory demo of the current
+directory. you may now change to that directory and compile the resulting files:
+
+cd demo
+javac -cp %ETCH_HOME%\lib\etch-java-runtime.jar *.java
+
+(Assuming windows; for unix, you only need to change the slash direction and
+change %ETCH_HOME% into \$ETCH_HOME.)
+
+To run the service (which initially won't do anything but make a connection and
+then close it, use the following two commands in separate shells (again, from
+the demo directory):
+
+java -cp %ETCH_HOME%\lib\etch-java-runtime.jar;.. demo.MainBlahListener
+
+java -cp %ETCH_HOME%\lib\etch-java-runtime.jar;.. demo.MainBlahClient
+
+(Again, assuming windows; for unix, make the changes detailed above and also
+change the semi-colon in the -cp spec to a colon.)
+
+The first command (java ... demo.MainBlahListener) starts the service listener
+which accepts connections. The second command runs a sample client, which makes
+a connection to the running listener and then closes it again.
+
+Once you've compiled the service and tested the result, you may implement your
+service by adding content to the etch file, adding implementation details to the
+Impl*.java and Main*.java files, and recompiling using the above steps. You may
+also load these files into an IDE such as eclipse or intellij, or use a build
+management system such as maven or ant. Remember, whenever you change the etch
+file, you must re-etch the service description and then re-javac all the java
+files.
+
+-------------------
+- Generated Files -
+-------------------
+
+Here is a description of the generated files for a service named Blah.
+
+Blah.java
+BlahClient.java
+BlahServer.java
+
+These three files are the generated interface classes. Service defined constants
+and types are in Blah.java, as well as direction "both" messages, which are
+messages which are shared between client and server. BlahClient.java and
+BlahServer.java are respectively the messages for just the direction client or
+server. You should not edit these files.
+
+RemoteBlah.java
+RemoteBlahClient.java
+RemoteBlahServer.java
+
+RemoteBlah*.java are the generated classes which implement the interfaces,
+with message implementations which encode the message type and arguments for
+transport to the connection peer and then receive and decode any response. You
+should not edit these files.
+
+StubBlah.java
+StubBlahClient.java
+StubBlahServer.java
+
+StubBlah*.java are the generated classes which receive messages encoded by
+RemoteBlah*.java and decode them, calling the appropriate message implementation
+and then encoding and sending any result. You should not edit these files.
+
+ValueFactoryBlah.java
+
+ValueFactoryBlah.java is a generated class which contains helper code to
+serialize service defined types and also the meta data which describes the
+messages and fields, field types, timeouts, etc. You should not edit this file.
+
+BaseBlah.java
+BaseBlahClient.java
+BaseBlahServer.java
+
+BaseBlah*.java are generated classes which implement the interfaces, with
+message implementations which do nothing but throw UnsupportedOperationException.
+They can be used to supply an implementation when you don't want to immediately
+implement all the messages. You should not edit these files.
+
+ImplBlahClient.java
+ImplBlahServer.java
+
+ImplBlah*.java are the generated sample client and server implementation
+classes. They extend the generated base classes. These are used by the sample
+main programs to provide implementations of the client or server messages. Edit
+these files as you wish to implement your service (overriding the default
+implementations from the generated base classes).
+
+BlahHelper.java
+
+BlahHelper.java is a generated class which includes static methods to help
+construct transports for client and listener. You should not edit this file.
+
+MainBlahClient.java
+MainBlahListener.java
+
+MainBlah*.java are the generated sample client and server main programs.
+They show how to setup a transport and start it and how to create a session.
+Edit these files as you wish to implement your service, or just copy the code
+into your own application.
+