diff options
Diffstat (limited to 'sca-cpp/branches/lightweight-sca/kernel')
27 files changed, 7539 insertions, 0 deletions
diff --git a/sca-cpp/branches/lightweight-sca/kernel/Makefile.am b/sca-cpp/branches/lightweight-sca/kernel/Makefile.am new file mode 100644 index 0000000000..e6a7fdb2b3 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/Makefile.am @@ -0,0 +1,50 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + + + +includedir = $(prefix)/include/kernel +include_HEADERS = *.hpp + +string_test_SOURCES = string-test.cpp + +noinst_test_LTLIBRARIES = libdynlib-test.la +noinst_testdir = `pwd`/tmp +libdynlib_test_la_SOURCES = dynlib-test.cpp +noinst_DATA = libdynlib-test${libsuffix} +libdynlib-test${libsuffix}: + ln -s .libs/libdynlib-test${libsuffix} + +kernel_test_SOURCES = kernel-test.cpp + +lambda_test_SOURCES = lambda-test.cpp + +mem_test_SOURCES = mem-test.cpp + +parallel_test_SOURCES = parallel-test.cpp + +xml_test_SOURCES = xml-test.cpp +xml_test_LDFLAGS = -lxml2 + +xsd_test_SOURCES = xsd-test.cpp +xsd_test_LDFLAGS = -lxml2 + +hash_test_SOURCES = hash-test.cpp + +noinst_PROGRAMS = string-test kernel-test lambda-test hash-test mem-test parallel-test xml-test xsd-test +TESTS = string-test kernel-test lambda-test hash-test mem-test parallel-test xml-test + diff --git a/sca-cpp/branches/lightweight-sca/kernel/config.hpp b/sca-cpp/branches/lightweight-sca/kernel/config.hpp new file mode 100644 index 0000000000..944b9629e7 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/config.hpp @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_config_hpp +#define tuscany_config_hpp + +#include "ap_config.h" +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION + +#include "../config.h" + +/** + * Platform configuration and debug functions. + */ +namespace tuscany +{ + +/** + * Attribute used to mark unused parameters. + */ +#ifndef unused +#define unused __attribute__ ((unused)) +#endif + +/** + * Compiler feature detection. + */ +#ifdef __clang__ + +#if __has_feature(cxx_lambdas) +#define HAS_CXX0X_LAMBDAS 1 +#endif + +#else + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4) +#define HAS_CXX0X_LAMBDAS 1 +#endif + +#endif + +/** + * Debug utilities. + */ +#ifdef WANT_MAINTAINER_MODE + +/** + * Strict compile warnings. + */ +#define WANT_MAINTAINER_WARNINGS + +/** + * Fast fail assertion. + */ +#define WANT_MAINTAINER_ASSERT + +/** + * Debug log. + */ +#define WANT_MAINTAINER_LOG + +/** + * Add string watch members to important classes to help watch them in a debugger. + */ +//#define WANT_MAINTAINER_WATCH + +/** + * Maintain counters of important objects to help test garbage collection. + */ +//#define WANT_MAINTAINER_COUNTERS + +#ifdef WANT_MAINTAINER_COUNTERS + +bool debug_inc(long int& c) { + c++; + return true; +} + +bool debug_dec(long int& c) { + c--; + return true; +} + +#else + +#define debug_inc(c) +#define debug_dec(c) + +#endif + +#else + +#define debug_inc(c) +#define debug_dec(c) + +#endif + +} +#endif /* tuscany_config_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/dynlib-test.cpp b/sca-cpp/branches/lightweight-sca/kernel/dynlib-test.cpp new file mode 100644 index 0000000000..419fa29db5 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/dynlib-test.cpp @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test library. + */ + +#include "function.hpp" + +namespace tuscany { +namespace test { + + const int cppsquare(int x) { + return x * x; + } + +} +} + +extern "C" { + + const int csquare(const int x) { + return tuscany::test::cppsquare(x); + } + + const tuscany::lambda<int(const int)> csquarel() { + return tuscany::lambda<int(const int)>(tuscany::test::cppsquare); + } + +} diff --git a/sca-cpp/branches/lightweight-sca/kernel/dynlib.hpp b/sca-cpp/branches/lightweight-sca/kernel/dynlib.hpp new file mode 100644 index 0000000000..69359b4dae --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/dynlib.hpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_dlib_hpp +#define tuscany_dlib_hpp + +/** + * Simple dynamic library access functions. + */ + +#include <dlfcn.h> + +#include "function.hpp" +#include "gc.hpp" +#include "monad.hpp" + +namespace tuscany { + +/** + * OS specific dynamic library file extension. + */ +#ifdef IS_DARWIN +const string dynlibExt(".dylib"); +#else +const string dynlibExt(".so"); +#endif + +/** + * Represents a reference to a dynamic library. + */ +class lib { +public: + lib() : h(NULL), owner(false) { + } + + lib(const string& name) : name(name), h(dlopen(c_str(name), RTLD_NOW)), owner(true) { + if (h == NULL) + h = mkfailure<void*>(string("Could not load library: ") + name + ": " + dlerror()); + } + + lib(const lib& l) : name(l.name), h(l.h), owner(false) { + } + + const lib& operator=(const lib& l) { + if(this == &l) + return *this; + name = l.name; + h = l.h; + owner = false; + return *this; + } + + ~lib() { + if (!owner) + return; + if (!hasContent(h) || content(h) == NULL) + return; + dlclose(content(h)); + } + +private: + template<typename S> friend const failable<lambda<S> > dynlambda(const string& name, const lib& l); + + string name; + failable<void*> h; + bool owner; +}; + +/** + * Find a lambda function in a dynamic library. + */ +template<typename S> const failable<lambda<S> > dynlambda(const string& name, const lib& l) { + if (!hasContent(l.h)) + return mkfailure<lambda<S>>(l.h); + const void* s = dlsym(content(l.h), c_str(name)); + if (s == NULL) + return mkfailure<lambda<S> >(string("Could not load symbol: ") + name); + return lambda<S>((S*)s); +} + +} +#endif /* tuscany_dlib_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/element.hpp b/sca-cpp/branches/lightweight-sca/kernel/element.hpp new file mode 100644 index 0000000000..27b5af8691 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/element.hpp @@ -0,0 +1,304 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_element_hpp +#define tuscany_element_hpp + +/** + * Functions to help represent data as lists of elements and attributes. + */ + +#include "list.hpp" +#include "value.hpp" + +namespace tuscany +{ + +/** + * Tags used to tag lists of elements and attributes. + */ +const value attribute("attribute"); +const value element("element"); +const string atsign("@"); + +/** + * Returns true if a value is an element. + */ +bool isElement(const value& v) { + if (!isList(v) || isNil(v) || element != car<value>(v)) + return false; + return true; +} + +/** + * Returns true if a value is an attribute. + */ +bool isAttribute(const value& v) { + if (!isList(v) || isNil(v) || attribute != car<value>(v)) + return false; + return true; +} + +/** + * Returns the name of an attribute. + */ +const value attributeName(const list<value>& l) { + return cadr(l); +} + +/** + * Returns the value of an attribute. + */ +const value attributeValue(const list<value>& l) { + return caddr(l); +} + +/** + * Returns the name of an element. + */ +const value elementName(const list<value>& l) { + return cadr(l); +} + +/** + * Returns true if an element has children. + */ +const bool elementHasChildren(const list<value>& l) { + return !isNil(cddr(l)); +} + +/** + * Returns the children of an element. + */ +const list<value> elementChildren(const list<value>& l) { + return cddr(l); +} + +/** + * Returns true if an element has a value. + */ +const bool elementHasValue(const list<value>& l) { + const list<value> r = reverse(l); + if (isSymbol(car(r))) + return false; + if(isList(car(r)) && !isNil((list<value>)car(r)) && isSymbol(car<value>(car(r)))) + return false; + return true; +} + +/** + * Returns the value of an element. + */ +const value elementValue(const list<value>& l) { + return car(reverse(l)); +} + +/** + * Convert an element to a value. + */ +const bool elementToValueIsList(const value& v) { + if (!isList(v)) + return false; + const list<value> l = v; + return (isNil(l) || !isSymbol(car(l))); +} + +const value elementToValue(const value& t) { + const list<value> elementsToValues(const list<value>& e); + + // Convert an attribute + if (isTaggedList(t, attribute)) + return mklist<value>(c_str(atsign + attributeName(t)), attributeValue(t)); + + // Convert an element + if (isTaggedList(t, element)) { + + // Convert an element's value + if (elementHasValue(t)) { + + // Convert a single value + if (!elementToValueIsList(elementValue(t))) + return mklist(elementName(t), elementValue(t)); + + // Convert a list value + return cons(elementName(t), mklist<value>(elementsToValues(elementValue(t)))); + } + + // Convert an element's children + return cons(elementName(t), elementsToValues(elementChildren(t))); + } + + // Convert a value + if (!isList(t)) + return t; + return elementsToValues(t); +} + +/** + * Convert a list of elements to a list of values. + */ +const bool elementToValueIsSymbol(const value& v) { + if (!isList(v)) + return false; + const list<value> l = v; + if (isNil(l)) + return false; + if (!isSymbol(car(l))) + return false; + return true; +} + +const list<value> elementToValueGroupValues(const value& v, const list<value>& l) { + if (isNil(l) || !elementToValueIsSymbol(v) || !elementToValueIsSymbol(car(l))) + return cons(v, l); + if (car<value>(car(l)) != car<value>(v)) + return cons(v, l); + if (!elementToValueIsList(cadr<value>(car(l)))) { + const value g = mklist<value>(car<value>(v), mklist<value>(isList(cadr<value>(v))? (value)cdr<value>(v) : cadr<value>(v), isList(cadr<value>(car(l)))? (value)cdr<value>(car(l)) : cadr<value>(car(l)))); + return elementToValueGroupValues(g, cdr(l)); + } + const value g = mklist<value>(car<value>(v), cons<value>(isList(cadr<value>(v))? (value)cdr<value>(v) : cadr<value>(v), (list<value>)cadr<value>(car(l)))); + return elementToValueGroupValues(g, cdr(l)); + +} + +const list<value> elementsToValues(const list<value>& e) { + if (isNil(e)) + return e; + return elementToValueGroupValues(elementToValue(car(e)), elementsToValues(cdr(e))); +} + +/** + * Convert a value to an element. + */ +const value valueToElement(const value& t) { + const list<value> valuesToElements(const list<value>& l); + + // Convert a name value pair + if (isList(t) && !isNil((list<value>)t) && isSymbol(car<value>(t))) { + const value n = car<value>(t); + const value v = isNil(cdr<value>(t))? value() : cadr<value>(t); + + // Convert a single value to an attribute or an element + if (!isList(v)) { + if (substr(n, 0, 1) == atsign) + return mklist<value>(attribute, c_str(substr(n, 1)), v); + return mklist(element, n, v); + } + + // Convert a list value + if (isNil((list<value>)v) || !isSymbol(car<value>(v))) + return cons(element, cons(n, mklist<value>(valuesToElements(v)))); + + // Convert a nested name value pair value + return cons(element, cons(n, valuesToElements(cdr<value>(t)))); + } + + // Convert a value + if (!isList(t)) + return t; + return valuesToElements(t); +} + +/** + * Convert a list of values to a list of elements. + */ +const list<value> valuesToElements(const list<value>& l) { + if (isNil(l)) + return l; + return cons<value>(valueToElement(car(l)), valuesToElements(cdr(l))); +} + +/** + * Returns a selector lambda function which can be used to filter + * elements against the given element pattern. + */ +struct selectorLambda { + const list<value> select; + selectorLambda(const list<value>& s) : select(s) { + } + const bool evalSelect(const list<value>& s, const list<value> v) const { + if (isNil(s)) + return true; + if (isNil(v)) + return false; + if (car(s) != car(v)) + return false; + return evalSelect(cdr(s), cdr(v)); + } + const bool operator()(const value& v) const { + if (!isList(v)) + return false; + return evalSelect(select, v); + } +}; + +const lambda<bool(const value&)> selector(const list<value> s) { + return selectorLambda(s); +} + +/** + * Returns the value of the attribute with the given name. + */ +struct filterAttribute { + const value name; + filterAttribute(const value& n) : name(n) { + } + const bool operator()(const value& v) const { + return isAttribute(v) && attributeName((list<value>)v) == name; + } +}; + +const value attributeValue(const value& name, const value& l) { + const list<value> f = filter<value>(filterAttribute(name), list<value>(l)); + if (isNil(f)) + return value(); + return caddr<value>(car(f)); +} + +/** + * Returns child elements with the given name. + */ +struct filterElement { + const value name; + filterElement(const value& n) : name(n) { + } + const bool operator()(const value& v) const { + return isElement(v) && elementName((list<value>)v) == name; + } +}; + +const value elementChildren(const value& name, const value& l) { + return filter<value>(filterElement(name), list<value>(l)); +} + +/** + * Return the child element with the given name. + */ +const value elementChild(const value& name, const value& l) { + const list<value> f = elementChildren(name, l); + if (isNil(f)) + return value(); + return car(f); +} + +} +#endif /* tuscany_element_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/fstream.hpp b/sca-cpp/branches/lightweight-sca/kernel/fstream.hpp new file mode 100644 index 0000000000..4f7f5152aa --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/fstream.hpp @@ -0,0 +1,404 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_fstream_hpp +#define tuscany_fstream_hpp + +/** + * File based streams. + */ + +#include <stdio.h> +#include <stdarg.h> + +#ifdef WANT_HTTPD_LOG +#include <apr_strings.h> +#include <apr_fnmatch.h> +#include <apr_lib.h> +#define APR_WANT_STRFUNC +#include <apr_want.h> +#include <apr_base64.h> + +#include <httpd.h> +// Hack to workaround compile error with CLang/LLVM +#undef strtoul +// Hack to workaround compile error with HTTPD 2.3.8 +#define new new_ +#include <http_config.h> +#undef new +#include <http_main.h> +#include <http_log.h> + +#else + +#include <time.h> +#include <sys/time.h> +#include <unistd.h> + +#ifdef WANT_THREADS +#include <pthread.h> +#endif + +#endif + +#include "string.hpp" +#include "stream.hpp" + +namespace tuscany { + +/* + * Output stream backed by a FILE. + */ +class ofstream : public ostream { +public: + ofstream(const string& path) : file(fopen(c_str(path), "wb")), owner(true) { + } + + ofstream(FILE* file) : file(file), owner(false) { + } + + ofstream(const ofstream& os) : file(os.file), owner(false) { + } + + const ofstream& operator=(const ofstream& os) { + if(this == &os) + return *this; + file = os.file; + owner = false; + return *this; + } + + ~ofstream() { + if (!owner) + return; + if (file == NULL) + return; + fclose(file); + } + + const bool fail() { + return file == NULL; + } + + ofstream& vprintf(const char* fmt, ...) { + va_list args; + va_start (args, fmt); + vfprintf (file, fmt, args); + va_end (args); + return *this; + } + + ofstream& write(const string& s) { + fwrite(c_str(s), 1, length(s), file); + return *this; + } + + ofstream& flush() { + fflush(file); + return *this; + } + +private: + FILE* file; + bool owner; +}; + +/* + * Input stream backed by a FILE. + */ +class ifstream : public istream { +public: + ifstream(const string& path) : file(fopen(c_str(path), "rb")), owner(true) { + } + + ifstream(FILE* file) : file(file), owner(false) { + } + + ifstream(const ifstream& is) : file(is.file), owner(false) { + } + + const ifstream& operator=(const ifstream& is) { + if(this == &is) + return *this; + file = is.file; + owner = false; + return *this; + } + + ~ifstream() { + if (!owner) + return; + if (file == NULL) + return; + fclose(file); + } + + const size_t read(void* buf, size_t size) { + return fread(buf, 1, size, file); + } + + const bool eof() { + return feof(file); + } + + const bool fail() { + return file == NULL; + } + + const int get() { + return fgetc(file); + } + + const int peek() { + int c = fgetc(file); + if (c == -1) + return c; + ungetc(c, file); + return c; + } + +private: + FILE* file; + bool owner; +}; + +/** + * Standard streams. + */ +ofstream cout(stdout); +ofstream cerr(stderr); +ifstream cin(stdin); + +/** + * Streams used for logging. + */ + +#ifdef WANT_HTTPD_LOG + +/* + * HTTPD-based log stream. + */ +class loghstream : public ostream { +public: + loghstream(const int level) : level(level), len(0) { + } + + ~loghstream() { + } + + ostream& vprintf(const char* fmt, ...) { + va_list args; + va_start (args, fmt); + const int l = vsnprintf(buf + len, (sizeof(buf) - 1) - len, fmt, args); + va_end (args); + len += l; + if (len > (int)(sizeof(buf) - 1)) + len = sizeof(buf) - 1; + return *this; + } + + ostream& write(const string& s) { + if (s != "\n") + return this->vprintf("%s", c_str(s)); + buf[len] = '\0'; + ap_log_error(NULL, 0, -1, level, 0, ap_server_conf, "%s", buf); + len = 0; + return *this; + } + + ostream& flush() { + return *this; + } + +private: + const int level; + int len; + char buf[2049]; +}; + +/** + * Info and failure log streams. + */ +loghstream cinfo(APLOG_INFO); +loghstream cfailure(APLOG_ERR); + +#ifdef WANT_MAINTAINER_LOG + +/** + * Debug log stream. + */ +loghstream cdebug(APLOG_DEBUG); + +/** + * Return true if debug log is enabled. + */ +#define debug_islogging() (bool)(APLOG_MODULE_IS_LEVEL(ap_server_conf, APLOG_NO_MODULE, APLOG_DEBUG)) + +#endif + +#else + +/** + * Format the current time. + */ +const string logTime() { + struct timeval tv; + gettimeofday(&tv, NULL); + const time_t t = tv.tv_sec; + const tm* lt = localtime(&t); + char ft[32]; + strftime(ft, 20, "%a %b %d %H:%M:%S", lt); + sprintf(ft + 19, ".%06lu ", (unsigned long)tv.tv_usec); + strftime(ft + 27, 5, "%Y", lt); + return ft; +} + +/* + * File-based log stream. + */ +class logfstream : public ostream { +public: + logfstream(FILE* file, const string& type) : file(file), type(type), head(false) { + } + + logfstream(const logfstream& os) : file(os.file), type(os.type), head(os.head) { + } + + ~logfstream() { + } + + ostream& vprintf(const char* fmt, ...) { + whead(); + va_list args; + va_start (args, fmt); + vfprintf (file, fmt, args); + va_end (args); + return *this; + } + + ostream& write(const string& s) { + whead(); + fwrite(c_str(s), 1, length(s), file); + if (s == "\n") + head = false; + return *this; + } + + ostream& flush() { + fflush(file); + return *this; + } + +private: + FILE* file; + const string type; + bool head; + + const unsigned long tid() const { +#ifdef WANT_THREADS + return (unsigned long)pthread_self(); +#else + return 0; +#endif + } + + ostream& whead() { + if (head) + return *this; + head = true; + *this << "[" << logTime() << "] [" << type << "] [pid " << (unsigned long)getpid() << ":tid " << tid() << "] "; + return *this; + } +}; + +/** + * Info and failure log streams. + */ +logfstream cinfo(stderr, "info"); +logfstream cfailure(stderr, "error"); + +#ifdef WANT_MAINTAINER_LOG + +/** + * Debug log stream. + */ +logfstream cdebug(stderr, "debug"); + +/** + * Return true if debug log is enabled. + */ +bool debug_isLoggingSet = false; +bool debug_isLoggingEnv = false; + +const bool debug_isLogging() { + if (debug_isLoggingSet) + return debug_isLoggingEnv; + debug_isLoggingEnv = getenv("TUSCANY_DEBUG_LOG") != NULL; + return debug_isLoggingEnv; +} + +#define debug_islogging() debug_isLogging() + +#endif + +#endif + +#ifdef WANT_MAINTAINER_LOG + +/** + * Log a debug message. + */ +const bool debugLog(const string& msg) { + gc_scoped_pool p; + cdebug << msg << endl; + return true; +} + +/** + * Log a debug message and a value. + */ +template<typename V> const bool debugLog(const V& v, const string& msg) { + gc_scoped_pool p; + cdebug << msg << ": " << v << endl; + return true; +} + +/** + * Log a debug message and two values. + */ +template<typename V, typename W> const bool debugLog(const V& v, const W& w, const string& msg) { + gc_scoped_pool p; + cdebug << msg << ": " << v << " : " << w << endl; + return true; +} + +#define debug(...) do { if (debug_islogging()) tuscany::debugLog(__VA_ARGS__); } while(0) + +#else + +#define debug_islogging() false +#define debug(...) + +#endif + +} + +#endif /* tuscany_fstream_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/function.hpp b/sca-cpp/branches/lightweight-sca/kernel/function.hpp new file mode 100644 index 0000000000..60117dab98 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/function.hpp @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_function_hpp +#define tuscany_function_hpp + +/** + * Lambda function type. + */ + +#include <utility> +#include "fstream.hpp" +#include "gc.hpp" +#include "config.hpp" + +namespace tuscany { + +#ifdef WANT_MAINTAINER_COUNTERS + +/** + * Debug counters. + */ +long int countProxies; +long int countFProxies = 0; +long int countCProxies = 0; +long int countLambdas = 0; +long int countELambdas = 0; +long int countCLambdas = 0; +long int countFLambdas = 0; + +bool resetLambdaCounters() { + countLambdas = countELambdas = countCLambdas = countFLambdas = countProxies = countFProxies = countCProxies = 0; + return true; +} + +bool checkLambdaCounters() { + return countLambdas == 0; +} + +bool printLambdaCounters() { + cout << "countLambdas " << countLambdas << endl; + cout << "countELambdas " << countELambdas << endl; + cout << "countFLambdas " << countFLambdas << endl; + cout << "countCLambdas " << countCLambdas << endl; + cout << "countProxies " << countProxies << endl; + cout << "countFProxies " << countFProxies << endl; + cout << "countCProxies " << countCProxies << endl; + return true; +} + +#else + +#define resetLambdaCounters() +#define checkLambdaCounters() true +#define printLambdaCounters() + +#endif + +/** + * Lambda function type. + */ + +template<typename R, typename... P> class Callable { +public: + Callable() { + } + + virtual const size_t size() const = 0; + + virtual const R operator()(P... p) const = 0; + + virtual ~Callable() { + } + + template<typename F> class Proxy: public Callable { + public: + Proxy(const F& f) : function(f) { + debug_inc(countProxies); + debug_inc(countFProxies); + } + + Proxy(const Proxy& p) : function(p.function) { + debug_inc(countProxies); + debug_inc(countCProxies); + } + + ~Proxy() { + debug_dec(countProxies); + } + + virtual const R operator() (P... p) const { + return function(std::forward<P>(p)...); + } + + virtual const size_t size() const { + return sizeof(function); + } + + private: + const F function; + }; +}; + +template<typename S> class lambda; + +template<typename R, typename... P> class lambda<R(P...)> { +public: + lambda() : callable(0) { + debug_inc(countLambdas); + debug_inc(countELambdas); + } + + template<typename F> lambda(const F f) { + debug_inc(countLambdas); + debug_inc(countFLambdas); + + typedef typename CallableType::template Proxy<F> ProxyType; + callable = gc_ptr<CallableType>(new (gc_new<ProxyType>()) ProxyType(f)); + } + + lambda(const lambda& l) { + debug_inc(countLambdas); + debug_inc(countCLambdas); + callable = l.callable; + } + + const lambda& operator=(const lambda& l) { + if (this == &l) + return *this; + callable = l.callable; + return *this; + } + + ~lambda() { + debug_dec(countLambdas); + } + + const bool operator==(const lambda& l) const { + if (this == &l) + return true; + return callable == l.callable; + } + + const bool operator!=(const lambda& l) const { + return !this->operator==(l); + } + + const R operator()(P... p) const { + return (*callable)(std::forward<P>(p)...); + } + + template<typename S> friend ostream& operator<<(ostream&, const lambda<S>&); + template<typename S> friend const bool isNil(const lambda<S>& l); + +private: + typedef Callable<R,P...> CallableType; + gc_ptr<CallableType> callable; +}; + +template<typename S> ostream& operator<<(ostream& out, const lambda<S>& l) { + return out << "lambda::" << l.callable; +} + +/** + * Return true if a lambda is nil. + */ +template<typename S> const bool isNil(const lambda<S>& l) { + return ((void*)l.callable) == 0; +} + +/** + * Curry a lambda function. + */ +template<typename R, typename T, typename... P> class curried { +public: + curried(const lambda<R(T, P...)>& f, const T& v): v(v), f(f) { + } + + const R operator()(P... p) const { + return f(v, std::forward<P>(p)...); + } + +private: + const T v; + const lambda<R(T, P...)>f; +}; + +template<typename R, typename T, typename... P> const lambda<R(P...)> curry(const lambda<R(T, P...)>& f, const T& t) { + return curried<R, T, P...>(f, t); +} + +template<typename R, typename T, typename U, typename... P> const lambda<R(P...)> curry(const lambda<R(T, U, P...)>& f, const T& t, const U& u) { + return curry(curry(f, t), u); +} + +template<typename R, typename T, typename U, typename V, typename... P> const lambda<R(P...)> curry(const lambda<R(T, U, V, P...)>& f, const T& t, const U& u, const V& v) { + return curry(curry(curry(f, t), u), v); +} + +/** + * A lambda function that returns the given value. + */ +template<typename T> class returnResult { +public: + returnResult(const T& v) : + v(v) { + } + const T operator()() const { + return v; + } +private: + const T v; +}; + +template<typename T> const lambda<T()> result(const T& v) { + return returnResult<T> (v); +} + +} +#endif /* tuscany_function_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/gc.hpp b/sca-cpp/branches/lightweight-sca/kernel/gc.hpp new file mode 100644 index 0000000000..32ad8160cc --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/gc.hpp @@ -0,0 +1,500 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_gc_hpp +#define tuscany_gc_hpp + +/** + * Garbage collected memory management, using APR memory pools. + */ + +#include "config.hpp" +#ifdef WANT_MALLOC_MMAP +#include <sys/mman.h> +#include <malloc.h> +#endif +#include <stdlib.h> +#include <apr_general.h> +#include <apr_pools.h> +#include <apr_strings.h> +#include <assert.h> +#include <new> +#ifdef WANT_THREADS +#include <pthread.h> +#endif + +namespace tuscany +{ + +#ifdef WANT_MAINTAINER_ASSERT + +/** + * Force a core dump on assertion violation. + */ +bool assertOrFail(const bool expr) { + if (!expr) + abort(); + return true; +} + +#else + +#define assertOrFail(expr) + +#endif + +/** + * Pointer to a value. + */ +template<typename T> class gc_ptr { +public: + gc_ptr(T* ptr = NULL) throw() : ptr(ptr) { + } + + ~gc_ptr() throw() { + } + + gc_ptr(const gc_ptr& r) throw() : ptr(r.ptr) { + } + + gc_ptr& operator=(const gc_ptr& r) throw() { + if(this == &r) + return *this; + ptr = r.ptr; + return *this; + } + + const bool operator==(const gc_ptr& r) const throw() { + if (this == &r) + return true; + return ptr == r.ptr; + } + + const bool operator==(T* p) const throw() { + return ptr == p; + } + + const bool operator!=(const gc_ptr& r) const throw() { + return !this->operator==(r); + } + + const bool operator!=(T* p) const throw() { + return !this->operator==(p); + } + + T& operator*() const throw() { + return *ptr; + } + + T* operator->() const throw() { + return ptr; + } + + operator T*() const throw() { + return ptr; + } + + T* ptr; +}; + +/** + * Initialize APR. + */ +class gc_apr_context_t { +public: + gc_apr_context_t() { + apr_initialize(); + } +} gc_apr_context; + +/** + * Garbage collected APR memory pool. + */ +class gc_pool { +public: + gc_pool() : apr_pool(NULL) { + } + + gc_pool(apr_pool_t* p) : apr_pool(p) { + } + + gc_pool(const gc_pool& pool) : apr_pool(pool.apr_pool) { + } + + gc_pool& operator=(const gc_pool& pool) { + if (this == &pool) + return *this; + apr_pool = pool.apr_pool; + return *this; + } + +private: + friend apr_pool_t* pool(const gc_pool& pool); + friend class gc_global_pool_t; + friend class gc_child_pool; + friend class gc_local_pool; + friend class gc_scoped_pool; + + apr_pool_t* apr_pool; +}; + +/** + * Return the APR pool used by a gc_pool. + */ +apr_pool_t* pool(const gc_pool& pool) { + return pool.apr_pool; +} + +/** + * Maintain a stack of memory pools. + */ +#ifdef WANT_THREADS + +class gc_pool_stack_t { +public: + gc_pool_stack_t() { + int rc = pthread_key_create(&key, NULL); + assertOrFail(rc == 0); + } + + operator apr_pool_t*() const { + return static_cast<apr_pool_t*>(pthread_getspecific(key)); + } + + const gc_pool_stack_t& operator=(apr_pool_t* p) { + pthread_setspecific(key, p); + return *this; + } + +private: + pthread_key_t key; +} gc_pool_stack; + +#else +apr_pool_t* gc_pool_stack = NULL; +#endif + +/** + * Push a pool onto the stack. + */ +apr_pool_t* gc_push_pool(apr_pool_t* pool) { + apr_pool_t* p = gc_pool_stack; + gc_pool_stack = pool; + return p; +} + +/** + * Pop a pool from the stack. + */ +apr_pool_t* gc_pop_pool(apr_pool_t* pool) { + apr_pool_t* p = gc_pool_stack; + gc_pool_stack = pool; + return p; +} + +/** + * Return the current memory pool. + */ +apr_pool_t* gc_current_pool() { + apr_pool_t* p = gc_pool_stack; + if (p != NULL) + return p; + + // Create a parent pool for the current thread + apr_pool_create(&p, NULL); + assertOrFail(p != NULL); + gc_push_pool(p); + return p; +} + +/** + * A child memory pool, which will be destroyed when its parent pool is destroyed. + */ +class gc_child_pool : public gc_pool { +public: + + gc_child_pool() : gc_pool(NULL), owner(true) { + apr_pool_create(&apr_pool, gc_current_pool()); + assertOrFail(apr_pool != NULL); + } + + gc_child_pool(const gc_child_pool& p) : gc_pool(p.apr_pool), owner(false) { + } + + const gc_child_pool& operator=(const gc_child_pool& p) { + if(this == &p) + return *this; + apr_pool = p.apr_pool; + owner = false; + return *this; + } + + +private: + bool owner; +}; + +/** + * A local pool scope, which will be destroyed when exiting the current scope. + */ +class gc_local_pool : public gc_pool { +public: + + gc_local_pool() : gc_pool(NULL), owner(true) { + apr_pool_create(&apr_pool, gc_current_pool()); + assertOrFail(apr_pool != NULL); + } + + ~gc_local_pool() { + if (owner) + apr_pool_destroy(apr_pool); + } + + gc_local_pool(const gc_local_pool& p) : gc_pool(p.apr_pool), owner(false) { + } + + const gc_local_pool& operator=(const gc_local_pool& p) { + if(this == &p) + return *this; + apr_pool = p.apr_pool; + owner = false; + return *this; + } + +private: + bool owner; +}; + +/** + * A memory pool scope, used to setup a scope in which a particular pool will be + * used for all allocations. Will be destroyed when existing the current scope. + */ +class gc_scoped_pool : public gc_pool { +public: + + gc_scoped_pool() : gc_pool(NULL), prev(gc_current_pool()), owner(true) { + apr_pool_create(&apr_pool, prev); + assertOrFail(apr_pool != NULL); + gc_push_pool(apr_pool); + } + + gc_scoped_pool(apr_pool_t* p) : gc_pool(p), prev(gc_current_pool()), owner(false) { + gc_push_pool(apr_pool); + } + + ~gc_scoped_pool() { + if (owner) + apr_pool_destroy(apr_pool); + gc_pop_pool(prev); + } + + gc_scoped_pool(const gc_scoped_pool& p) : gc_pool(p.apr_pool), prev(p.prev), owner(false) { + } + + const gc_scoped_pool& operator=(const gc_scoped_pool& p) { + if(this == &p) + return *this; + apr_pool = p.apr_pool; + prev = p.prev; + owner = false; + return *this; + } + +private: + apr_pool_t* prev; + bool owner; +}; + +/** + * Allocates a pointer to an object allocated from a memory pool and + * register a cleanup callback for it. + */ +template<typename T> apr_status_t gc_pool_cleanup(void* v) { + T* t = (T*)v; + t->~T(); + return APR_SUCCESS; +} + +template<typename T> T* gc_new(apr_pool_t* p) { + void* gc_new_ptr = apr_palloc(p, sizeof(T)); + assertOrFail(gc_new_ptr != NULL); + apr_pool_cleanup_register(p, gc_new_ptr, gc_pool_cleanup<T>, apr_pool_cleanup_null) ; + return (T*)(gc_new_ptr); +} + +template<typename T> T* gc_new(const gc_pool& p) { + return gc_new<T>(pool(p)); +} + +template<typename T> T* gc_new() { + return gc_new<T>(gc_current_pool()); +} + +template<typename T> apr_status_t gc_pool_acleanup(void* v) { + size_t* m = static_cast<size_t*>(v); + size_t n = *m; + T* t = (T*)(m + 1); + for (size_t i = 0; i < n; i++, t++) + t->~T(); + return APR_SUCCESS; +} + +template<typename T> T* gc_anew(apr_pool_t* p, size_t n) { + size_t* gc_anew_ptr = static_cast<size_t*>(apr_palloc(p, sizeof(size_t) + sizeof(T) * n)); + assertOrFail(gc_anew_ptr != NULL); + *gc_anew_ptr = n; + apr_pool_cleanup_register(p, gc_anew_ptr, gc_pool_acleanup<T>, apr_pool_cleanup_null) ; + return (T*)(gc_anew_ptr + 1); +} + +template<typename T> T* gc_anew(const gc_pool& p, size_t n) { + return gc_anew<T>(pool(p), n); +} + +template<typename T> T* gc_anew(size_t n) { + return gc_anew<T>(gc_current_pool(), n); +} + +/** + * Allocate an array of chars. + */ +char* gc_cnew(apr_pool_t* p, size_t n) { + char* gc_cnew_ptr = static_cast<char*>(apr_palloc(p, n)); + assertOrFail(gc_cnew_ptr != NULL); + return gc_cnew_ptr; +} + +char* gc_cnew(size_t n) { + return gc_cnew(gc_current_pool(), n); +} + +/** + * Pool based equivalent of the standard malloc function. + */ +void* gc_pool_malloc(size_t n) { + size_t* ptr = static_cast<size_t*>(apr_palloc(gc_current_pool(), sizeof(size_t) + n)); + assertOrFail(ptr != NULL); + *ptr = n; + return ptr + 1; +} + +/** + * Pool based equivalent of the standard realloc function. + */ +void* gc_pool_realloc(void* ptr, size_t n) { + size_t size = *(static_cast<size_t*>(ptr) - 1); + size_t* rptr = static_cast<size_t*>(apr_palloc(gc_current_pool(), sizeof(size_t) + n)); + assertOrFail(rptr != NULL); + *rptr = n; + memcpy(rptr + 1, ptr, size < n? size : n); + return rptr + 1; +} + +/** + * Pool based equivalent of the standard free function. + */ +void gc_pool_free(unused void* ptr) { + // Memory allocated from a pool is freed when the pool is freed +} + +/** + * Pool based equivalent of the standard strdup function. + */ +char* gc_pool_strdup(const char* str) { + char* dptr = static_cast<char*>(gc_pool_malloc(strlen(str) + 1)); + assertOrFail(dptr != NULL); + strcpy(dptr, str); + return dptr; +} + +#ifdef WANT_MALLOC_MMAP + +/** + * Mmap based memory allocation functions. + */ + +/** + * Mmap based equivalent of the standard malloc function. + */ +void* gc_mmap_malloc(size_t n, unused const void* caller) { + //printf("gc_mmap_malloc %d", n); + size_t* ptr = static_cast<size_t*>(mmap(NULL, sizeof(size_t) + n, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); + assertOrFail(ptr != NULL); + *ptr = n; + //printf(" %p\n", ptr + 1); + return ptr + 1; +} + +/** + * Mmap based equivalent of the standard realloc function. + */ +void* gc_mmap_realloc(void* ptr, size_t n, const void* caller) { + if (ptr == NULL) + return gc_mmap_malloc(n, caller);; + //printf("gc_mmap_realloc %p %d", ptr, n); + size_t size = *(static_cast<size_t*>(ptr) - 1); + size_t* rptr = static_cast<size_t*>(mremap(static_cast<size_t*>(ptr) - 1, sizeof(size_t) + size, sizeof(size_t) + n, MREMAP_MAYMOVE, NULL)); + assertOrFail(rptr != NULL); + *rptr = n; + //printf(" %p\n", rptr + 1); + return rptr + 1; +} + +/** + * Mmap based equivalent of the standard free function. + */ +void gc_mmap_free(void* ptr, unused const void* caller) { + //printf("gc_mmap_free %p\n", ptr); + if (ptr == NULL) + return; + size_t size = *(static_cast<size_t*>(ptr) - 1); + munmap(static_cast<size_t*>(ptr) - 1, sizeof(size_t) + size); +} + +/** + * Mmap based equivalent of the standard memalign function. + */ +void* gc_mmap_memalign(unused size_t alignment, size_t n, unused const void* caller) { + //printf("gc_mmap_memalign %d %d\n", alignment, n); + return gc_mmap_malloc(n, caller); +} + +/** + * Install the mmap based memory allocation functions. + */ +void gc_mmap_init_hook(void) { + __malloc_hook = gc_mmap_malloc; + __realloc_hook = gc_mmap_realloc; + __free_hook = gc_mmap_free; + __memalign_hook = gc_mmap_memalign; +} + +#endif + +} + +#ifdef WANT_MALLOC_MMAP + +void (*__malloc_initialize_hook)(void) = tuscany::gc_mmap_init_hook; + +#endif + +#endif /* tuscany_gc_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/hash-test.cpp b/sca-cpp/branches/lightweight-sca/kernel/hash-test.cpp new file mode 100644 index 0000000000..4e6a3654e5 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/hash-test.cpp @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test hash functions. + */ + +#include <assert.h> +#include "string.hpp" +#include "hash.hpp" +#include "perf.hpp" + +namespace tuscany { + +bool testCrc32hash() { + const string key("This is a test key"); + unsigned int h = crc32hash(c_str(key), length(key)); + assert(h != 0); + return true; +} + +bool testTimes33hash() { + const string key("This is a test key"); + unsigned int h = times33hash(c_str(key), length(key)); + assert(h != 0); + return true; +} + +bool testMurmurhash() { + const string key("This is a test key"); + unsigned int h = murmurhash(c_str(key), length(key)); + assert(h != 0); + return true; +} + +bool testPortablemurmurhash() { + const string key("This is a test key"); + unsigned int h = portablemurmurhash(c_str(key), length(key)); + assert(h != 0); + return true; +} + +struct crc32hashTest { + const string key; + crc32hashTest(const string& key) : key(key) { + } + bool operator()() const { + unsigned int h = crc32hash(c_str(key), length(key)); + assert(h != 0); + return true; + } +}; + +struct times33hashTest { + const string key; + times33hashTest(const string& key) : key(key) { + } + bool operator()() const { + unsigned int h = times33hash(c_str(key), length(key)); + assert(h != 0); + return true; + } +}; + +struct murmurhashTest { + const string key; + murmurhashTest(const string& key) : key(key) { + } + bool operator()() const { + unsigned int h = murmurhash(c_str(key), length(key)); + assert(h != 0); + return true; + } +}; + +struct portablemurmurhashTest { + const string key; + portablemurmurhashTest(const string& key) : key(key) { + } + bool operator()() const { + unsigned int h = portablemurmurhash(c_str(key), length(key)); + assert(h != 0); + return true; + } +}; + +bool testHashPerf() { + const string key("This is a test key"); + const int count = 100000; + + const lambda<bool()> crc32 = crc32hashTest(key); + cout << "crc32hash test " << time(crc32, 5, count) << " ms" << endl; + const lambda<bool()> times33 = times33hashTest(key); + cout << "times33hash test " << time(times33, 5, count) << " ms" << endl; + const lambda<bool()> murmur = murmurhashTest(key); + cout << "murmurhash test " << time(murmur, 5, count) << " ms" << endl; + const lambda<bool()> portablemurmur = portablemurmurhashTest(key); + cout << "portable murmurhash test " << time(portablemurmur, 5, count) << " ms" << endl; + + return true; +} + +} + +int main() { + tuscany::gc_scoped_pool p; + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::testCrc32hash(); + tuscany::testTimes33hash(); + tuscany::testMurmurhash(); + tuscany::testPortablemurmurhash(); + tuscany::testHashPerf(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/kernel/hash.hpp b/sca-cpp/branches/lightweight-sca/kernel/hash.hpp new file mode 100644 index 0000000000..993511e3c2 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/hash.hpp @@ -0,0 +1,207 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_hash_hpp +#define tuscany_hash_hpp + +/** + * Fast hash functions. + */ + +#include <apr_hash.h> +#include <apr_memcache.h> + +namespace tuscany +{ + +/** + * Apache apr-util CRC32 hash function. + * + * See srclib/apr-util/memcache/apr_memcache.c from the Apache HTTPD + * source tree. Reproducing the comments from apr_memcache.c here: + * + * The crc32 functions and data were originally written by Spencer + * Garrett <srg@quick.com> and were gleaned from the PostgreSQL source + * tree at contrib/ltree/crc32.[ch] and from FreeBSD at + * src/usr.bin/cksum/crc32.c. + */ +const unsigned int crc32hash(const char* data, const size_t len) { + return (unsigned int)apr_memcache_hash_default(NULL, data, len); +} + +/** + * Apache apr tables default hash function. + * + * See srclib/apr/tables/apr_hash.c from the Apache HTTPD source tree. + * Reproducing the comments from apr_hash.c here: + * + * This is the popular `times 33' hash algorithm which is used by + * perl and also appears in Berkeley DB. This is one of the best + * known hash functions for strings because it is both computed + * very fast and distributes very well. + * + * The originator may be Dan Bernstein but the code in Berkeley DB + * cites Chris Torek as the source. The best citation I have found + * is "Chris Torek, Hash function for text in C, Usenet message + * <27038@mimsy.umd.edu> in comp.lang.c , October, 1990." in Rich + * Salz's USENIX 1992 paper about INN which can be found at + * <http://citeseer.nj.nec.com/salz92internetnews.html>. + * + * The magic of number 33, i.e. why it works better than many other + * constants, prime or not, has never been adequately explained by + * anyone. So I try an explanation: if one experimentally tests all + * multipliers between 1 and 256 (as I did while writing a low-level + * data structure library some time ago) one detects that even + * numbers are not useable at all. The remaining 128 odd numbers + * (except for the number 1) work more or less all equally well. + * They all distribute in an acceptable way and this way fill a hash + * table with an average percent of approx. 86%. + * + * If one compares the chi^2 values of the variants (see + * Bob Jenkins ``Hashing Frequently Asked Questions'' at + * http://burtleburtle.net/bob/hash/hashfaq.html for a description + * of chi^2), the number 33 not even has the best value. But the + * number 33 and a few other equally good numbers like 17, 31, 63, + * 127 and 129 have nevertheless a great advantage to the remaining + * numbers in the large set of possible multipliers: their multiply + * operation can be replaced by a faster operation based on just one + * shift plus either a single addition or subtraction operation. And + * because a hash function has to both distribute good _and_ has to + * be very fast to compute, those few numbers should be preferred. + * + * -- Ralf S. Engelschall <rse@engelschall.com> + */ +const unsigned int times33hash(const char* data, const size_t len) { + apr_ssize_t l = len; + return apr_hashfunc_default(data, &l); +} + +/** + * A very fast, non-cryptographic hash suitable for general hash-based + * lookup. See http://murmurhash.googlepages.com/ for more details. + * + * Original code by Austin Appleby, released to the public domain and under + * the MIT license. + * + * Compiles down to ~52 instructions on x86. + * Passes chi^2 tests for practically all keysets & bucket sizes. + * Excellent avalanche behavior. Maximum bias is under 0.5%. + * Passes Bob Jenkin's frog.c torture-test. No collisions possible for 4 byte + * keys, no small 1 to 7 bit differentials. + */ +const unsigned int murmurhash(const char* key, const size_t klen) { + unsigned int len = (unsigned int)klen; + const unsigned int seed = 0; + + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + const unsigned int m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + unsigned int h = seed ^ len; + + // Mix 4 bytes at a time into the hash + const unsigned char* data = (const unsigned char*)key; + while(len >= 4) { + unsigned int k = *(unsigned int*)(void*)data; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + data += 4; + len -= 4; + } + + // Handle the last few bytes of the input array + switch(len) { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +/** + * An endian and alignment neutral, but half the speed, version of + * the murmur hash. + */ +const unsigned int portablemurmurhash(const char* key, const size_t klen) { + unsigned int len = (unsigned int)klen; + const unsigned int seed = 0; + + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + const unsigned int m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + unsigned int h = seed ^ len; + + // Mix 4 bytes at a time into the hash + const unsigned char* data = (const unsigned char *)key; + while(len >= 4) { + unsigned int k; + k = data[0]; + k |= data[1] << 8; + k |= data[2] << 16; + k |= data[3] << 24; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + data += 4; + len -= 4; + } + + // Handle the last few bytes of the input array + switch(len) { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +const unsigned int hashselect(const unsigned int hash, const unsigned int max) { + return hash % max; +} + +} +#endif /* tuscany_hash_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/kernel-test.cpp b/sca-cpp/branches/lightweight-sca/kernel/kernel-test.cpp new file mode 100644 index 0000000000..4d2ca2ba81 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/kernel-test.cpp @@ -0,0 +1,614 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test kernel functions. + */ + +#include <assert.h> +#include "string.hpp" +#include "sstream.hpp" +#include "function.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "dynlib.hpp" +#include "perf.hpp" + +namespace tuscany { + +struct inc { + int i; + inc(int i) : + i(i) { + } + const int operator()(const int x) const { + return x + i; + } +}; + +const int square(const int x) { + return x * x; +} + +int mapLambda(lambda<int(const int)> f, int v) { + return f(v); +} + +bool testLambda() { + const lambda<int(const int)> sq(square); + assert(sq(2) == 4); + assert(mapLambda(sq, 2) == 4); + assert(mapLambda(square, 2) == 4); + + const lambda<int(const int)> incf(inc(10)); + assert(incf(1) == 11); + assert(mapLambda(incf, 1) == 11); + assert(mapLambda(inc(10), 1) == 11); + + lambda<int(const int)> l; + l = incf; + assert(l(1) == 11); + l = square; + assert(l(2) == 4); + return true; +} + +bool testLambdaGC() { + resetLambdaCounters(); + { + gc_scoped_pool gc; + testLambda(); + } + assert(checkLambdaCounters()); + return true; +} + +int countElements = 0; + +struct Element { + int i; + + Element() : i(0) { + countElements++; + } + + Element(int i) : i(i) { + countElements++; + } + + Element(const Element& o) : i(o.i) { + countElements++; + } + + ~Element() { + countElements--; + } + + const bool operator==(const Element& o) const { + return o.i == i; + } +}; +ostream& operator<<(ostream& out, const Element& v) { + out << v.i ; + return out; +} + +bool testCons() { + assert(car(cons(2, mklist(3))) == 2); + assert(car(cdr(cons(2, mklist(3)))) == 3); + assert(isNil(cdr(cdr(cons(2, mklist(3)))))); + + assert(cons(Element(1), mklist(Element(2))) == mklist(Element(1), Element(2))); + return true; +} + +bool testListGC() { + resetLambdaCounters(); + resetListCounters(); + countElements = 0; + { + gc_scoped_pool gc; + testCons(); + } + assert(checkLambdaCounters()); + assert(checkListCounters()); + assert(countElements == 0); + return true; +} + +bool testOut() { + ostringstream os1; + os1 << list<int> (); + assert(str(os1) == "()"); + + ostringstream os2; + os2 << mklist(1, 2, 3); + assert(str(os2) == "(1 2 3)"); + return true; +} + +bool testEquals() { + assert(list<int>() == list<int>()); + assert(mklist(1, 2) == mklist(1, 2)); + assert(list<int>() != mklist(1, 2)); + assert(mklist(1, 2, 3) == mklist(1, 2, 3)); + assert(mklist(1, 2) != mklist(1, 2, 3)); + return true; +} + +bool testLength() { + assert(0 == length(list<int>())); + assert(1 == length(mklist(1))); + assert(2 == length(cons(1, mklist(2)))); + return true; +} + +bool testAppend() { + assert(car(append(mklist(1), mklist(2))) == 1); + assert(car(cdr(append(mklist(1), mklist(2)))) == 2); + assert(car(cdr(cdr(append(mklist(1), mklist(2, 3))))) == 3); + assert(isNil(cdr(cdr(cdr(append(mklist(1), mklist(2, 3))))))); + + assert(list<int>() + 1 + 2 + 3 == mklist(1, 2, 3)); + return true; +} + +struct Complex { + int x; + int y; + Complex() { + } + Complex(int x, int y) : + x(x), y(y) { + } +}; +ostream& operator<<(ostream& out, const Complex& v) { + out << "[" << v.x << ":" << v.y << "]"; + return out; +} + +bool testComplex() { + const list<Complex> p = mklist(Complex(1, 2), Complex(3, 4)); + assert(car(p).x == 1); + assert(car(cdr(p)).x == 3); + assert(isNil(cdr(cdr(p)))); + return true; +} + +bool testMap() { + assert(isNil(map<int, int>(square, list<int>()))); + + const list<int> m = map<int, int>(square, mklist(2, 3)); + assert(car(m) == 4); + assert(car(cdr(m)) == 9); + + return true; +} + +const int add(const int x, const int y) { + return x + y; +} + +bool testReduce() { + const lambda<int(const int, const int)> r(add); + assert(reduce(r, 0, mklist(1, 2, 3)) == 6); + return true; +} + +bool isPositive(const int x) { + if(x >= 0) + return true; + else + return false; +} + +bool testFilter() { + assert(car(filter<int>(isPositive, mklist(1, -1, 2, -2))) == 1); + assert(cadr(filter<int>(isPositive, mklist(1, -1, 2, -2))) == 2); + return true; +} + +bool testMember() { + assert(isNil(member(4, mklist(1, 2, 3)))); + assert(car(member(1, mklist(1, 2, 3))) == 1); + assert(car(member(2, mklist(1, 2, 3))) == 2); + assert(car(member(3, mklist(1, 2, 3))) == 3); + return true; +} + +bool testReverse() { + assert(isNil(reverse(list<int>()))); + assert(car(reverse(mklist(1, 2, 3))) == 3); + assert(cadr(reverse(mklist(1, 2, 3))) == 2); + return true; +} + +bool testListRef() { + assert(listRef(mklist(1), 0) == 1); + assert(listRef(mklist(1, 2, 3), 0) == 1); + assert(listRef(mklist(1, 2, 3), 1) == 2); + assert(listRef(mklist(1, 2, 3), 2) == 3); + return true; +} + +bool testAssoc() { + const list<list<string> > l = mklist(mklist<string>("x", "X"), mklist<string>("a", "A"), mklist<string>("y", "Y"), mklist<string>("a", "AA")); + assert(assoc<string>("a", l) == mklist<string>("a", "A")); + assert(isNil(assoc<string>("z", l))); + + const list<list<value> > u = mklist(mklist<value>("x", "X"), mklist<value>("a", "A"), mklist<value>("y", "Y"), mklist<value>("a", "AA")); + assert(assoc<value>("a", u) == mklist<value>("a", "A")); + + const list<value> v = mklist<value>(mklist<value>("x", "X"), mklist<value>("a", "A"), mklist<value>("y", "Y"), mklist<value>("a", "AA")); + assert(assoc<value>("a", v) == mklist<value>("a", "A")); + return true; +} + +bool testZip() { + const list<string> k = mklist<string>("x", "a", "y", "a"); + const list<string> v = mklist<string>("X", "A", "Y", "AA"); + const list<list<string> > z = mklist(k, v); + const list<list<string> > u = mklist(mklist<string>("x", "X"), mklist<string>("a", "A"), mklist<string>("y", "Y"), mklist<string>("a", "AA")); + assert(zip(k, v) == u); + assert(unzip(u) == z); + return true; +} + +bool testTokenize() { + assert(tokenize("/", "") == list<string>()); + assert(tokenize("/", "aaa") == mklist<string>("aaa")); + assert(tokenize("/", "aaa/bbb/ccc/ddd") == mklist<string>("aaa", "bbb", "ccc", "ddd")); + assert(tokenize("/", "/bbb/ccc/ddd") == mklist<string>("", "bbb", "ccc", "ddd")); + assert(tokenize("/", "/bbb/ccc/") == mklist<string>("", "bbb", "ccc")); + assert(tokenize("/", "/bbb//ccc/") == mklist<string>("", "bbb", "", "ccc")); + assert(tokenize("/", "abc/def/") == mklist<string>("abc", "def")); + + assert(join("/", list<string>()) == ""); + assert(join("/", mklist<string>("aaa")) == "aaa"); + assert(join("/", mklist<string>("aaa", "bbb", "ccc", "ddd")) == "aaa/bbb/ccc/ddd"); + assert(join("/", mklist<string>("", "bbb", "ccc", "ddd")) == "/bbb/ccc/ddd"); + assert(join("/", mklist<string>("bbb", "ccc", "")) == "bbb/ccc/"); + assert(join("/", mklist<string>("bbb", "", "ccc")) == "bbb//ccc"); + return true; +} + +double testSeqMap(double x) { + return x; +} + +double testSeqReduce(unused double v, double accum) { + return accum + 1.0; +} + +bool testSeq() { + resetLambdaCounters(); + resetListCounters(); + + list<double> s = seq(0.0, 1000.0); + assert(1001 == length(s)); + + assert(1001 == length(map<double, double>(testSeqMap, s))); + + assert(801 == length(member(200.0, s))); + assert(201 == length(member(200.0, reverse(s)))); + + assert(1001 == (reduce<double, double>(testSeqReduce, 0.0, s))); + return true; +} + +value valueSquare(list<value> x) { + return (int)car(x) * (int)car(x); +} + +bool testValue() { + assert(value(true) == value(true)); + assert(value(1) == value(1)); + assert(value("abcd") == value("abcd")); + lambda<value(const list<value>&)> vl(valueSquare); + assert(value(vl) == value(vl)); + assert(value(mklist<value>(1, 2)) == value(mklist<value>(1, 2))); + + const list<value> v = mklist<value>(mklist<value>("x", "X"), mklist<value>("a", "A"), mklist<value>("y", "Y")); + assert(cadr((list<list<value> >)value(v)) == mklist<value>("a", "A")); + + const value pv(gc_ptr<value>(new (gc_new<value>()) value(1))); + assert(*(gc_ptr<value>)pv == value(1)); + + const list<value> lpv = mklist<value>(gc_ptr<value>(new (gc_new<value>()) value(1)), gc_ptr<value>(new (gc_new<value>()) value(2))); + assert(*(gc_ptr<value>)car(lpv) == value(1)); + return true; +} + +bool testValueGC() { + resetLambdaCounters(); + resetListCounters(); + resetValueCounters(); + { + gc_scoped_pool gc; + testValue(); + } + assert(checkValueCounters()); + assert(checkLambdaCounters()); + assert(checkListCounters()); + return true; +} + +bool testTree() { + const list<value> t = mktree<value>("a", list<value>(), list<value>()); + const list<value> ct = constree<value>("d", constree<value>("f", constree<value>("c", constree<value>("e", constree<value>("b", t))))); + const list<value> mt = mktree(mklist<value>("d", "f", "c", "e", "b", "a")); + assert(mt == ct); + const list<value> l = flatten<value>(mt); + assert(length(l) == 6); + assert(car(l) == "a"); + assert(car(reverse(l)) == "f"); + const list<value> bt = mkbtree<value>(l); + assert(car(bt) == "c"); + return true; +} + +const list<value> lta(const string& x) { + return mklist<value>(c_str(x), c_str(x + x)); +} + +bool testTreeAssoc() { + const list<value> t = mktree<value>(lta("a"), list<value>(), list<value>()); + const list<value> at = constree<value>(lta("d"), constree<value>(lta("f"), constree<value>(lta("c"), constree<value>(lta("e"), constree<value>(lta("b"), t))))); + const list<value> l = flatten<value>(at); + assert(length(l) == 6); + assert(car(l) == mklist<value>("a", "aa")); + assert(car(reverse(l)) == mklist<value>("f", "ff")); + const list<value> bt = mkbtree<value>(l); + assert(car(bt) == mklist<value>("c", "cc")); + assert(assoctree<value>("a", bt) == mklist<value>("a", "aa")); + assert(assoctree<value>("b", bt) == mklist<value>("b", "bb")); + assert(assoctree<value>("f", bt) == mklist<value>("f", "ff")); + assert(isNil(assoctree<value>("x", bt))); + return true; +} + +double fib_aux(double n, double a, double b) { + if(n == 0.0) + return a; + return fib_aux(n - 1.0, b, a + b); +} + +double fib(double n) { + return fib_aux(n, 0.0, 1.0); +} + +struct fibMapPerf { + const bool operator()() const { + list<double> s = seq(0.0, 999.0); + list<double> r = map<double, double>(fib, s); + assert(1000 == length(r)); + return true; + } +}; + +struct nestedFibMapPerf { + const lambda<double(const double)> fib; + nestedFibMapPerf(const lambda<double(const double)>& fib) : fib(fib) { + } + const bool operator()() const { + list<double> s = seq(0.0, 999.0); + list<double> r = map<double, double>(fib, s); + assert(1000 == length(r)); + return true; + } +}; + +bool testCppPerf() { + { + const lambda<bool()> fml = fibMapPerf(); + cout << "Fibonacci map test " << (time(fml, 1, 1) / 1000) << " ms" << endl; + } + + { + struct nested { + static double fib(double n) { + struct nested { + static double fib_aux(double n, double a, double b) { + if(n == 0.0) + return a; + return fib_aux(n - 1.0, b, a + b); + } + }; + return nested::fib_aux(n, 0.0, 1.0); + } + }; + + const lambda<bool()> nfml = nestedFibMapPerf(lambda<double(const double)>(nested::fib)); + cout << "Nested Fibonacci map test " << (time(nfml, 1, 1) / 1000) << " ms" << endl; + } + return true; +} + +const id<int> idF(const int v) { + return v * 2; +} + +const id<int> idG(const int v) { + return v * 3; +} + +const id<int> idH(const int v) { + return idF(v) >> idG; +} + +bool testIdMonad() { + const id<int> m(2); + assert(m >> idF == idF(2)); + assert(m >> unit<int>() == m); + assert(m >> idF >> idG == m >> idH); + return true; +} + +const maybe<int> maybeF(const int v) { + return v * 2; +} + +const maybe<int> maybeG(const int v) { + return v * 3; +} + +const maybe<int> maybeH(const int v) { + return maybeF(v) >> maybeG; +} + +bool testMaybeMonad() { + const maybe<int> m(2); + assert(m >> maybeF == maybeF(2)); + assert((m >> just<int>()) == m); + assert(m >> maybeF >> maybeG == m >> maybeH); + + assert(maybe<int>() >> maybeF >> maybeG == maybe<int>()); + return true; +} + +const failable<int> failableF(const int v) { + return v * 2; +} + +const failable<int> failableG(const int v) { + return v * 3; +} + +const failable<int> failableH(const int v) { + return failableF(v) >> failableG; +} + +bool testFailableMonad() { + const failable<int> m(2); + assert(m >> failableF == failableF(2)); + assert((m >> success<int, string, int>()) == m); + assert(m >> failableF >> failableG == m >> failableH); + + cout << "Failable monad test... " << endl; + const failable<int> ooops = mkfailure<int>("test", 500); + assert(reason(ooops) == "test"); + assert(rcode(ooops) == 500); + assert(ooops >> failableF >> failableG == ooops); + + const failable<value> vooops = mkfailure<value>(ooops); + assert(reason(vooops) == "test"); + assert(rcode(vooops) == 500); + + const value v = value(vooops); + assert(car<value>(v) == value()); + assert(cadr<value>(v) == string("test")); + assert(caddr<value>(v) == value((double)500)); + return true; +} + +struct tickInc { + const double v; + tickInc(const double v) : v(v) { + } + const scp<int, double> operator()(int s) const { + return scp<int, double>(s + 1, v); + } +}; + +const state<int, double> tick(const double v) { + return transformer<int, double>(tickInc(v)); +} + +const state<int, double> stateF(const double v) { + return result<int, double>(v * 2.0) >> tick; +} + +const state<int, double> stateG(const double v) { + return result<int, double>(v + 5); +} + +const state<int, double> stateH(const double v) { + return stateF(v) >> stateG; +} + +bool testStateMonad() { + const lambda<state<int, double>(const double)> r(result<int, double>); + + state<int, double> m = result<int, double>(2.0); + assert((m >> stateF)(0) == stateF(2.0)(0)); + assert(1 == (int)(m >> stateF)(0)); + assert((m >> r)(0) == m(0)); + assert((m >> stateF >> stateG)(0) == (m >> stateH)(0)); + + return true; +} + +bool testDynLib() { + const lib dl(string("./libdynlib-test") + dynlibExt); + const failable<lambda<int(const int)> > sq(dynlambda<int(const int)>("csquare", dl)); + assert(hasContent(sq)); + lambda<int(const int)> l(content(sq)); + assert(l(2) == 4); + + const failable<lambda<lambda<int(const int)>()> > sql(dynlambda<lambda<int(const int)>()>("csquarel", dl)); + assert(hasContent(sql)); + lambda<lambda<int(const int)>()> ll(content(sql)); + assert(ll()(3) == 9); + return true; +} + +} + +int main() { + tuscany::gc_scoped_pool p; + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::testLambda(); + tuscany::testLambdaGC(); + tuscany::testCons(); + tuscany::testListGC(); + tuscany::testOut(); + tuscany::testEquals(); + tuscany::testLength(); + tuscany::testAppend(); + tuscany::testComplex(); + tuscany::testMap(); + tuscany::testReduce(); + tuscany::testFilter(); + tuscany::testMember(); + tuscany::testReverse(); + tuscany::testListRef(); + tuscany::testAssoc(); + tuscany::testZip(); + tuscany::testTokenize(); + tuscany::testSeq(); + tuscany::testValue(); + tuscany::testValueGC(); + tuscany::testTree(); + tuscany::testTreeAssoc(); + tuscany::testCppPerf(); + tuscany::testIdMonad(); + tuscany::testMaybeMonad(); + tuscany::testFailableMonad(); + tuscany::testStateMonad(); + tuscany::testDynLib(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/kernel/lambda-test.cpp b/sca-cpp/branches/lightweight-sca/kernel/lambda-test.cpp new file mode 100644 index 0000000000..05a16c2eb8 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/lambda-test.cpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +/** + * Test C++0x lambda expressions. + */ + +#include <assert.h> +#include "function.hpp" +#include "sstream.hpp" +#include "fstream.hpp" +#include "perf.hpp" + +namespace tuscany { + +#ifdef HAS_CXX0X_LAMBDAS + +const lambda<const int(const int)> inc(const int i) { + return [=](const int x)->const int { + return x + i; + }; +} + +const int square(const int x) { + return x * x; +} + +int mapLambda(const lambda<const int(const int)> f, int v) { + return f(v); +} + +bool testLambda() { + const lambda<const int(const int)> sq = square; + assert(sq(2) == 4); + assert(mapLambda(square, 2) == 4); + assert(mapLambda(sq, 2) == 4); + assert(mapLambda([](const int x)->const int { return x * x; }, 2) == 4); + + const lambda<int(int)> incf = inc(10); + assert(incf(1) == 11); + assert(mapLambda(incf, 1) == 11); + assert(mapLambda(inc(10), 1) == 11); + + lambda<const int(const int)> l; + l = incf; + assert(l(1) == 11); + l = square; + assert(l(2) == 4); + return true; +} + +const double fib_aux(const double n, const double a, const double b) { + return n == 0.0? a : fib_aux(n - 1.0, b, a + b); +} + +const bool fibMapPerf() { + list<double> s = seq(0.0, 4999.0); + list<double> r = map<double, double>([](const double n)->const double { return fib_aux(n, 0.0, 1.0); }, s); + assert(5000 == length(r)); + return true; +} + +bool testCppPerf() { + cout << "Fibonacci map test " << (time([]()->const bool { return fibMapPerf(); }, 1, 1) / 5000) << " ms" << endl; + return true; +} + +#endif +} + +int main() { + tuscany::gc_scoped_pool p; + tuscany::cout << "Testing..." << tuscany::endl; + +#ifdef HAS_CXX0X_LAMBDAS + tuscany::testLambda(); + tuscany::testCppPerf(); +#else + tuscany::cout << "Skipped C++0x lambda tests" << tuscany::endl; +#endif + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/kernel/list.hpp b/sca-cpp/branches/lightweight-sca/kernel/list.hpp new file mode 100644 index 0000000000..d3736de62c --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/list.hpp @@ -0,0 +1,617 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_list_hpp +#define tuscany_list_hpp + +/** + * Simple list functions. + */ + +#include <assert.h> +#include "string.hpp" +#include "fstream.hpp" +#include "function.hpp" + +namespace tuscany { + +#ifdef WANT_MAINTAINER_COUNTERS + +/** + * Debug utilities. Counters used to track instances of lists. + */ +long countLists = 0; +long countILists = 0; +long countCLists = 0; +long countELists = 0; + +bool resetListCounters() { + countLists = countILists = countCLists = countELists = 0; + return true; +} + +bool checkListCounters() { + return countLists == 0; +} + +bool printListCounters() { + cout << "countLists " << countLists << endl; + cout << "countELists " << countELists << endl; + cout << "countILists " << countILists << endl; + cout << "countCLists " << countCLists << endl; + return true; +} + +#else + +#define resetListCounters() +#define checkListCounters() true +#define printListCounters() + +#endif + +#ifdef WANT_MAINTAINER_WATCH + +/** + * Debug utilities. Macro used to write the contents of a list to + * a string, easier to watch in a debugger than the list itself. + */ +#define debug_watchList() do { \ + this->watch = watchList(*this); \ + } while (0) + +#else + +#define debug_watchList(); + +#endif + +/** + * A car/cdr lisp-like pair, base structure to construct lists. + */ + +template<typename T> class list { +public: + + list() { + debug_inc(countLists); + debug_inc(countELists); + debug_watchList(); + } + + list(const T car, const lambda<list<T>()>& cdr) : car(car), cdr(cdr) { + debug_inc(countLists); + debug_inc(countILists); + debug_watchList(); + } + + list(const list& p) : car(p.car), cdr(p.cdr) { + debug_inc(countLists); + debug_inc(countCLists); +#ifdef WANT_MAINTAINER_WATCH + watch = p.watch; +#endif + } + + const list<T>& operator=(const list<T>& p) { + if(this == &p) + return *this; + car = p.car; + cdr = p.cdr; +#ifdef WANT_MAINTAINER_WATCH + watch = p.watch; +#endif + return *this; + } + + ~list() { + debug_dec(countLists); + } + + const bool operator==(const list<T>& p) const { + if(this == &p) + return true; + if(isNil(cdr)) + return isNil(p.cdr); + if(isNil(p.cdr)) + return false; + if(!(car == p.car)) + return false; + if(cdr == p.cdr) + return true; + return cdr() == p.cdr(); + } + + const bool operator<(const list<T>& p) const { + if(this == &p) + return false; + if (isNil(cdr)) + return !isNil(p.cdr); + if (isNil(p.cdr)) + return false; + if (car < p.car) + return true; + if (car != p.car) + return false; + return cdr() < p.cdr(); + } + + const bool operator>(const list<T>& p) const { + if(this == &p) + return false; + if (isNil(cdr)) + return false; + if (isNil(p.cdr)) + return true; + if (car > p.car) + return true; + if (car != p.car) + return false; + return cdr() > p.cdr(); + } + + const bool operator!=(const list<T>& p) const { + return !this->operator==(p); + } + + operator const list<list<T> >() const { + return (list<list<T> >)T(*this); + } + +private: +#ifdef WANT_MAINTAINER_WATCH + template<typename X> friend const string watchList(const list<X>& p); + string watch; +#endif + + template<typename X> friend const bool isNil(const list<X>& p); + template<typename X> friend const X car(const list<X>& p); + template<typename X> friend const list<X> cdr(const list<X>& p); + + T car; + lambda<list<T>()> cdr; +}; + +#ifdef WANT_MAINTAINER_WATCH + +/** + * Debug utility used to write the contents of a list to a string, easier + * to watch than the list itself in a debugger. + */ +template<typename T> const string watchList(const list<T>& p) { + if(isNil(p)) + return "()"; + odebugstream os; + os << "(" << car(p) << " ...)"; + return str(os); +} + +#endif + +/** + * Returns true if the given list is nil. + */ +template<typename T> const bool isNil(const list<T>& p) { + return isNil(p.cdr); +} + +/** + * Write a list to an output stream. + */ +template<typename T> ostream& writeHelper(ostream& out, const list<T>& l) { + if (isNil(l)) + return out; + out << " " << car(l); + return writeHelper(out, cdr(l)); +} + +template<typename T> ostream& operator<<(ostream& out, const list<T>& l) { + if(isNil(l)) + return out << "()"; + out << "(" << car(l); + writeHelper<T>(out, cdr(l)); + return out << ")"; +} + +/** + * Construct a (lazy) list from a value and a lambda function that returns the cdr. + */ +template<typename T> const list<T> cons(const T& car, const lambda<list<T>()>& cdr) { + return list<T> (car, cdr); +} + +/** + * Construct a list from a value and a cdr list. + */ +template<typename T> const list<T> cons(const T& car, const list<T>& cdr) { + return list<T> (car, result(cdr)); +} + +/** + * Cons variations for use with the reduce and reduceRight functions. + */ +template<typename T> const list<T> lcons(const list<T>& cdr, const T& car) { + return cons<T>(car, cdr); +} + +template<typename T> const list<T> rcons(const T& car, const list<T>& cdr) { + return cons<T>(car, cdr); +} + +/** + * Construct a list of one value. + */ +template<typename T> const list<T> mklist(const T& car) { + return list<T> (car, result(list<T> ())); +} + +/** + * Construct a list of two values. + */ +template<typename T> const list<T> mklist(const T& a, const T& b) { + return cons(a, mklist(b)); +} + +/** + * Construct a list of three values. + */ +template<typename T> const list<T> mklist(const T& a, const T& b, const T& c) { + return cons(a, cons(b, mklist(c))); +} + +/** + * Construct a list of four values. + */ +template<typename T> const list<T> mklist(const T& a, const T& b, const T& c, const T& d) { + return cons(a, cons(b, cons(c, mklist(d)))); +} + +/** + * Construct a list of five values. + */ +template<typename T> const list<T> mklist(const T& a, const T& b, const T& c, const T& d, const T& e) { + return cons(a, cons(b, cons(c, cons(d, mklist(e))))); +} + +/** + * Construct a list of six values. + */ +template<typename T> const list<T> mklist(const T& a, const T& b, const T& c, const T& d, const T& e, const T& f) { + return cons(a, cons(b, cons(c, cons(d, cons(e, mklist(f)))))); +} + +/** + * Returns the car of a list. + */ +template<typename T> const T car(const list<T>& p) { + // Abort if trying to access the car of a nil list + assertOrFail(!isNil(p.cdr)); + return p.car; +} + +/** + * Returns the cdr of a list. + */ +template<typename T> const list<T> cdr(const list<T>& p) { + return p.cdr(); +} + +/** + * Returns the car of the cdr (the 2nd element) of a list. + */ +template<typename T> const T cadr(const list<T>& p) { + return car(cdr(p)); +} + +/** + * Returns the 3rd element of a list. + */ +template<typename T> const T caddr(const list<T>& p) { + return car(cdr(cdr(p))); +} + +/** + * Returns the 4th element of a list. + */ +template<typename T> const T cadddr(const list<T>& p) { + return car(cdr(cdr(cdr(p)))); +} + +/** + * Returns the 5th element of a list. + */ +template<typename T> const T caddddr(const list<T>& p) { + return car(cdr(cdr(cdr(cdr(p))))); +} + +/** + * Returns the 6th element of a list. + */ +template<typename T> const T cadddddr(const list<T>& p) { + return car(cdr(cdr(cdr(cdr(cdr(p)))))); +} + +/** + * Returns the 7th element of a list. + */ +template<typename T> const T caddddddr(const list<T>& p) { + return car(cdr(cdr(cdr(cdr(cdr(cdr(p))))))); +} + +/** + * Returns the 8th element of a list. + */ +template<typename T> const T cadddddddr(const list<T>& p) { + return car(cdr(cdr(cdr(cdr(cdr(cdr(cdr(p)))))))); +} + +/** + * Returns a list of elements from the 3rd to the end of a list. + */ +template<typename T> const list<T> cddr(const list<T>& p) { + return cdr(cdr(p)); +} + +/** + * Returns a list of elements from the 4th to the end of a list. + */ +template<typename T> const list<T> cdddr(const list<T>& p) { + return cdr(cdr(cdr(p))); +} + +/** + * Returns a list of elements from the 5th to the end of a list. + */ +template<typename T> const list<T> cddddr(const list<T>& p) { + return cdr(cdr(cdr(cdr(p)))); +} + +/** + * Returns a list of elements from the 6th to the end of a list. + */ +template<typename T> const list<T> cdddddr(const list<T>& p) { + return cdr(cdr(cdr(cdr(cdr(p))))); +} + +/** + * Returns a list of elements from the 7th to the end of a list. + */ +template<typename T> const list<T> cddddddr(const list<T>& p) { + return cdr(cdr(cdr(cdr(cdr(cdr(p)))))); +} + +/** + * Returns a list of elements from the 8th to the end of a list. + */ +template<typename T> const list<T> cdddddddr(const list<T>& p) { + return cdr(cdr(cdr(cdr(cdr(cdr(cdr(p))))))); +} + +/** + * Returns the length of a list. + */ +template<typename T> struct lengthRef { + const size_t operator()(const size_t c, const list<T>& p) { + if(isNil(p)) + return c; + return (*this)(c + 1, cdr(p)); + } +}; + +template<typename T> const size_t length(const list<T>& p) { + return lengthRef<T> ()(0, p); +} + +/** + * Appends a list and a lambda function returning a list. + */ +template<typename T> struct appendCdr { + const list<T> a; + const lambda<list<T>()> fb; + appendCdr(const list<T>& a, const lambda<list<T>()>& fb) : + a(a), fb(fb) { + } + const list<T> operator()() const { + return append(a, fb); + } +}; + +template<typename T> const list<T> append(const list<T>&a, const lambda<list<T>()>& fb) { + if(isNil(a)) + return fb(); + + return cons<T>(car(a), appendCdr<T> (cdr(a), fb)); +} + +/** + * Appends two lists. + */ +template<typename T> const list<T> append(const list<T>&a, const list<T>& b) { + return append(a, result(b)); +} + +/** + * Append a value to a list. + */ +template<typename T> const list<T> operator+(const list<T>& l, const T& v) { + return append(l, mklist(v)); +} + +template<typename T, typename V> const list<T> operator+(const list<T>& l, const V& v) { + return append(l, mklist<T>(v)); +} + +/** + * Map a lambda function on a list. + */ +template<typename T, typename R> const list<R> map(const lambda<R(const T)>& f, const list<T>& p) { + if(isNil(p)) + return list<R> (); + return cons(f(car(p)), map(f, cdr(p))); +} + +/** + * Run a reduce lambda function on a list. + */ +template<typename T, typename R> struct reduceAccumulate { + const lambda<R(const R&, const T&)> f; + reduceAccumulate(const lambda<R(const R, const T)>& f) : + f(f) { + } + R operator()(const R& acc, const list<T>& p) const { + if(isNil(p)) + return acc; + return (*this)(f(acc, car(p)), cdr(p)); + } +}; + +template<typename T, typename R> const R reduce(const lambda<R(const R, const T)>& f, const R& initial, const list<T>& p) { + return reduceAccumulate<T, R> (f)(initial, p); +} + +template<typename T, typename R> struct reduceRightAccumulate { + const lambda<R(const T&, const R&)> f; + reduceRightAccumulate(const lambda<R(const T, const R)>& f) : + f(f) { + } + R operator()(const list<T>& p, const R& acc) const { + if(isNil(p)) + return acc; + return (*this)(cdr(p), f(car(p), acc)); + } +}; + +template<typename T, typename R> const R reduceRight(const lambda<R(const T, const R)>& f, const R& initial, const list<T>& p) { + return reduceRightAccumulate<T, R> (f)(p, initial); +} + +/** + * Run a filter lambda function on a list. + */ +template<typename T> const list<T> filter(const lambda<bool(const T)>& f, const list<T>& p) { + if(isNil(p)) + return list<T> (); + if(f(car(p))) { + const lambda<list<T>(const lambda<bool(const T)>, const list<T>)> ff(filter<T>); + return cons(car(p), curry(ff, f, cdr(p))); + } + return filter(f, cdr(p)); +} + +/** + * Returns a list pointing to a member of a list. + */ +template<typename T> const list<T> member(const T& t, const list<T>& p) { + if(isNil(p)) + return list<T> (); + if(t == car(p)) + return p; + return member(t, cdr(p)); +} + +/** + * Reverse a list. + */ +template<typename T> const list<T> reverseIter(const list<T>& acc, const list<T>& p) { + if(isNil(p)) + return acc; + return reverseIter(cons(car(p), acc), cdr(p)); +} + +template<typename T> const list<T> reverse(const list<T>& p) { + return reverseIter(list<T> (), p); +} + +template<typename T> const list<T> seq(const T& start, const T& end); + +template<typename T> struct seqGenerate { + const T start; + const T end; + seqGenerate(const T& start, const T&end) : + start(start), end(end) { + } + const list<T> operator()() const { + return seq<T> (start, end); + } +}; + +/** + * Returns a sequence of values between the given bounds. + */ +template<typename T> const list<T> seq(const T& start, const T& end) { + if(start == end) + return mklist(start); + if(start < end) + return cons<T>(start, seqGenerate<T> (start + 1, end)); + return cons<T>(start, seqGenerate<T> (start - 1, end)); +} + +/** + * Returns the i-th element of a list. + */ +template<typename T> const T listRef(const list<T>& l, const size_t i) { + if (i == 0) + return car(l); + return listRef(cdr(l), i - 1); +} + +/** + * Returns the first pair matching a key from a list of key value pairs. + */ +template<typename T> const list<T> assoc(const T& k, const list<list<T> >& p) { + if(isNil(p)) + return list<T> (); + if(k == car(car(p))) + return car(p); + return assoc(k, cdr(p)); +} + +/** + * Returns a list of lists containing elements from two input lists. + */ +template<typename T> const list<list<T> > zip(const list<T>& a, const list<T>& b) { + if (isNil(a) || isNil(b)) + return list<list<T> >(); + return cons<list<T> >(mklist<T>(car(a), car(b)), zip(cdr(a), cdr(b))); +} + +/** + * Converts a list of key value pairs to a list containing the list of keys and the list of values. + */ +template<typename T> const list<T> unzipKeys(const list<list<T> >& l) { + if (isNil(l)) + return list<T>(); + return cons(car(car(l)), unzipKeys(cdr(l))); +} + +template<typename T> const list<T> unzipValues(const list<list<T> >& l) { + if (isNil(l)) + return list<T>(); + return cons(cadr(car(l)), unzipValues(cdr(l))); +} + +template<typename T> const list<list<T> > unzip(const list<list<T> >& l) { + return mklist<list<T> >(unzipKeys(l), unzipValues(l)); +} + +} + +#endif /* tuscany_list_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/mem-test.cpp b/sca-cpp/branches/lightweight-sca/kernel/mem-test.cpp new file mode 100644 index 0000000000..668dabe749 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/mem-test.cpp @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test memory allocation functions. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "gc.hpp" +#include "function.hpp" +#include "perf.hpp" + +namespace tuscany { + +int countElements = 0; +int maxElements = 0; + +class Element { +public: + Element() : i(0) { + countElements++; + if (countElements > maxElements) + maxElements = countElements; + } + + Element(int i) : i(i) { + countElements++; + if (countElements > maxElements) + maxElements = countElements; + } + + Element(const Element& o) : i(o.i) { + countElements++; + if (countElements > maxElements) + maxElements = countElements; + } + + ~Element() { + countElements--; + } + + const bool operator==(const Element& o) const { + return o.i == i; + } + +private: + friend ostream& operator<<(ostream& out, const Element& v); + + int i; + char c[20]; +}; + +ostream& operator<<(ostream& out, const Element& v) { + out << v.i ; + return out; +} + +bool poolAlloc(Element** p, const int count) { + if (count == 0) + return true; + p[count - 1] = new (gc_new<Element>()) Element(); + return poolAlloc(p, count - 1); +}; + +bool poolFree(Element** p, const int count) { + if (count == 0) + return true; + // Do nothing to free the element, but cycle through them just + // to get a fair comparison with the other memory alloc tests + return poolFree(p, count - 1); +}; + +struct poolAllocPerf { + const int n; + Element** p; + poolAllocPerf(const int n) : n(n), p(new Element*[n]) { + } + const bool operator()() const { + gc_scoped_pool gc; + poolAlloc(p, n); + return true; + } +}; + +bool testPoolAllocPerf() { + const int count = 10000; + const lambda<bool()> pl = poolAllocPerf(count); + maxElements = 0; + cout << "Memory pool alloc test " << (time(pl, 1, 1) / count) << " ms" << endl; + assert(countElements == 0); + assert(maxElements == count); + return true; +} + +bool stdAlloc(Element** p, const int count) { + if (count == 0) + return true; + p[count - 1] = new Element(); + return stdAlloc(p, count - 1); +}; + +bool stdFree(Element** p, const int count) { + if (count == 0) + return true; + delete p[count -1]; + return stdFree(p, count - 1); +}; + +struct stdAllocPerf { + const int n; + Element** p; + stdAllocPerf(const int n) : n(n), p(new Element*[n]) { + } + const bool operator()() const { + stdAlloc(p, n); + stdFree(p, n); + return true; + } +}; + +bool testStdAllocPerf() { + const int count = 10000; + const lambda<bool()> sl = stdAllocPerf(count); + maxElements = 0; + cout << "Memory standard alloc test " << (time(sl, 1, 1) / count) << " ms" << endl; + assert(countElements == 0); + assert(maxElements == count); + return true; +} + +} + +int main() { + tuscany::gc_scoped_pool p; + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::testPoolAllocPerf(); + tuscany::testStdAllocPerf(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/kernel/monad.hpp b/sca-cpp/branches/lightweight-sca/kernel/monad.hpp new file mode 100644 index 0000000000..b67e92ad79 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/monad.hpp @@ -0,0 +1,627 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_monad_hpp +#define tuscany_monad_hpp + +/** + * Simple monad implementations. + */ + +#include <execinfo.h> +#include <cxxabi.h> +#include "function.hpp" +#include "string.hpp" +#include "stream.hpp" +#include "sstream.hpp" +#include "fstream.hpp" + +namespace tuscany +{ + +/** + * Identity monad. Just wraps a value. + * To get the value in the monad, just cast it to the value type. + */ +template<typename V> class id { +public: + id(const V& v) : v(v) { + } + + const id<V>& operator=(const id<V>& m) { + if(this == &m) + return *this; + v = m.v; + return *this; + } + + const bool operator!=(const id<V>& m) const { + return !this->operator==(m); + } + + const bool operator==(const id<V>& m) const { + if (&m == this) + return true; + return v == m.v; + } + +private: + const V v; + + template<typename X> friend const X content(const id<X>& m); +}; + +/** + * Write an identity monad to a stream. + */ +template<typename V> ostream& operator<<(ostream& out, const id<V>& m) { + out << content(m); + return out; +} + +/** + * Returns the content of an identity monad. + */ +template<typename V> const V content(const id<V>& m) { + return m.v; +} + +/** + * Return an identity monad from a value. + */ +template<typename V> const id<V> mkunit(const V& v) { + return id<V>(v); +} + +template<typename V> const lambda<id<V>(const V)> unit() { + return mkunit<V>; +} + +/** + * Bind a function to an identity monad. Pass the value in the monad to the function. + */ +template<typename R, typename V> const id<R> operator>>(const id<V>& m, const lambda<id<R>(const V)>& f) { + return f(content(m)); +} + +template<typename R, typename V> const id<R> operator>>(const id<V>& m, const id<R> (* const f)(const V)) { + return f(content(m)); +} + +/** + * Maybe monad. Used to represent an optional value, which may be there or not. + * To get the value in the monad, just cast it to the value type. + */ +template<typename V> class maybe { +public: + maybe(const V& v) : hasv(true), v(v) { + } + + maybe() : hasv(false) { + } + + const maybe<V>& operator=(const maybe<V>& m) { + if(this == &m) + return *this; + hasv = m.hasv; + if (hasv) + v = m.v; + return *this; + } + + const bool operator!=(const maybe<V>& m) const { + return !this->operator==(m); + } + + const bool operator==(const maybe<V>& m) const { + if (this == &m) + return true; + if (!hasv) + return !m.hasv; + return m.hasv && v == m.v; + } + +private: + const bool hasv; + V v; + + template<typename X> friend const bool hasContent(const maybe<X>& m); + template<typename X> friend const X content(const maybe<X>& m); +}; + +/** + * Write a maybe monad to a stream. + */ +template<typename V> ostream& operator<<(ostream& out, const maybe<V>& m) { + if (!hasContent(m)) { + out << "nothing"; + return out; + } + out << content(m); + return out; +} + +/** + * Return a maybe monad with a value in it. + */ +template<typename V> const maybe<V> mkjust(const V& v) { + return maybe<V>(v); +} + +template<typename V> const lambda<maybe<V>(const V)> just() { + return mkjust<V>; +} + +/** + * Returns true if a maybe monad contains a content. + */ +template<typename V> const bool hasContent(const maybe<V>& m) { + return m.hasv; +} + +/** + * Returns the content of a maybe monad. + */ +template<typename V> const V content(const maybe<V>& m) { + return m.v; +} + +/** + * Bind a function to a maybe monad. Passes the value in the monad to the function + * if present, or does nothing if there's no value. + */ +template<typename R, typename V> const maybe<R> operator>>(const maybe<V>& m, const lambda<maybe<R>(const V)>& f) { + if (!hasContent(m)) + return m; + return f(content(m)); +} + +template<typename R, typename V> const maybe<R> operator>>(const maybe<V>& m, const maybe<R> (* const f)(const V)) { + if (!hasContent(m)) + return m; + return f(content(m)); +} + +/** + * Failable monad. Used to represent either a success value or a failure. + * To get the value in the monad, just cast it to the value type. + * To get the failure in the monad, cast it to the failure type. + */ +template<typename V, typename F = string, typename C = int> class failable { +public: + failable() : hasv(false), c(-1) { + } + + failable(const V& v) : hasv(true), v(v), c(-1) { + } + + failable(const failable<V, F, C>& m) : hasv(m.hasv), v(m.v), f(m.f), c(m.c) { + } + + const failable<V, F, C>& operator=(const failable<V, F, C>& m) { + if (&m == this) + return *this; + hasv = m.hasv; + v = m.v; + f = m.f; + c = m.c; + return *this; + } + + const bool operator!=(const failable<V, F, C>& m) const { + return !this->operator==(m); + } + + const bool operator==(const failable<V, F, C>& m) const { + if (this == &m) + return true; + if (!hasv) + return !m.hasv && f == m.f && c == m.c; + return m.hasv && v == m.v; + } + +private: + failable(const bool hasv, const F& f, const C& c) : hasv(hasv), f(f), c(c) { + } + + template<typename A, typename B, typename R> friend const bool hasContent(const failable<A, B, R>& m); + template<typename A, typename B, typename R> friend const A content(const failable<A, B, R>& m); + template<typename A, typename B, typename R> friend const B reason(const failable<A, B, R>& m); + template<typename A, typename B, typename R> friend const R rcode(const failable<A, B, R>& m); + template<typename A, typename B, typename R> friend const failable<A, B, R> mkfailure(const B& f, const R& c, const bool log); + template<typename A, typename B> friend const failable<A, B> mkfailure(const B& f, const int c, const bool log); + template<typename A> friend const failable<A> mkfailure(); + + bool hasv; + V v; + F f; + C c; +}; + +/** + * Write a failable monad to a stream. + */ +template<typename V, typename F, typename C> ostream& operator<<(ostream& out, const failable<V, F, C>& m) { + if (!hasContent(m)) { + out << reason(m) << " : " << rcode(m); + return out; + } + out << content(m); + return out; +} + +/** + * Returns a failable monad with a success value in it. + */ +template<typename V, typename F, typename C> const failable<V, F, C> mksuccess(const V& v) { + return failable<V, F, C>(v); +} + +template<typename V, typename F, typename C> const lambda<failable<V, F, C>(const V)> success() { + return mksuccess<V, F, C>; +} + +/** + * Demangle a C++ function name. + */ +const string demangleFrame(const char* fun) { + int status; + char* name = abi::__cxa_demangle(fun, 0, 0, &status); + if (name == NULL) + return fun; + const string s = name; + free(name); + return s; +} + +/** + * Format a backtrace frame. + */ +const char* formatFrameFile(const char* file) { + const char* s = strrchr(file, '/'); + return s == NULL? file : s + 1; +} + +const string formatFrame(const char* symbol) { +#ifdef __clang__ + // Mac OS X CLang/LLVM stack frame format + // 0 kernel-test 0x000000010d440179 _ZN7tuscany9mkfailureINS_5valueENS_6stringEiEEKNS_8failableIT_T0_T1_EERKS5_RKS6_b + 265 + char nb[3]; + char file[256]; + char addr[32]; + char fun[256]; + char offset[16]; + if (sscanf(symbol, "%2s %255s %31s %255s %*[+] %15s", nb, file, addr, fun, offset) == 5) { + char buf[1024]; + if (debug_islogging()) + sprintf(buf, "%.255s %.31s %.511s + %.15s", formatFrameFile(file), addr, c_str(demangleFrame(fun)), offset); + else + sprintf(buf, "%.255s %.31s", formatFrameFile(file), addr); + return buf; + } +#else + // Linux GCC stack frame format + // ./kernel-test(_ZN7tuscany9mkfailureINS_5valueENS_6stringEiEEKNS_8failableIT_T0_T1_EERKS5_RKS6_b+0x23d) [0xb7197afd] + char file[256]; + char fun[256]; + char offset[16]; + char addr[32]; + if (sscanf(symbol, "%[^(]%*[(]%[^+]%*[+]%[^)]%*[)] %*[[]%[^]]%*[]]", file, fun, offset, addr) == 4) { + char buf[1024]; + if (debug_islogging()) + sprintf(buf, "%.255s %.31s %.511s + %.15s", formatFrameFile(file), addr, c_str(demangleFrame(fun)), offset); + else + sprintf(buf, "%.255s %.31s", formatFrameFile(file), addr); + return buf; + } + if (sscanf(symbol, "%[^(]%*[(]%*[^)]%*[)] %*[[]%[^]]%*[]]", file, addr) == 2) { + char buf[512]; + sprintf(buf, "%.255s %.31s", formatFrameFile(file), addr); + return buf; + } + if (sscanf(symbol, "%[^(]%*[(]%*[)] %*[[]%[^]]%*[]]", file, addr) == 2) { + char buf[512]; + sprintf(buf, "%.255s %.31s", formatFrameFile(file), addr); + return buf; + } +#endif + return symbol; +} + +/** + * Log backtrace frames. + */ +const bool logFrames(char** symbols, const int frames, const bool log) { + if (frames == 0) + return true; +#ifdef WANT_MAINTAINER_LOG + if (!log) + debug(formatFrame(*symbols), "failable::backtrace"); +#endif + if (log) + cfailure << "failable::backtrace: " << formatFrame(*symbols) << endl; + return logFrames(symbols + 1, frames - 1, log); +} + +/** + * Log a backtrace. + */ +const bool logBacktrace(void** callstack, const int frames, const bool log) { + char** symbols = backtrace_symbols(callstack, frames); + logFrames(symbols, frames, log); + free(symbols); + return true; +} + +/** + * Returns a failable monad with a failure in it. + */ +template<typename V, typename F, typename C> const failable<V, F, C> mkfailure(const F& f, const C& c, const bool log = true) { +#ifdef WANT_MAINTAINER_LOG + if (!log) { + // Log the failure + debug(f, "failable::mkfailure"); + + // Log the call stack + void* callstack[16]; + const int frames = backtrace(callstack, 16); + logBacktrace(callstack, frames, log); + } +#endif + if (log) { + ostringstream os; + os << f; + if (length(str(os)) != 0) { + // Log the failure + cfailure << "failable::mkfailure: " << f << " : " << c << endl; + + // Print the call stack + void* callstack[16]; + const int frames = backtrace(callstack, 16); + logBacktrace(callstack, frames, log); + } + } + return failable<V, F, C>(false, f, c); +} + +template<typename V, typename F> const failable<V, F> mkfailure(const F& f, const int c = -1, const bool log = true) { + return mkfailure<V, F, int>(f, c, log); +} + +template<typename V> const failable<V> mkfailure(const char* f, const int c = -1, const bool log = true) { + return mkfailure<V, string, int>(string(f), c, log); +} + +template<typename V> const failable<V> mkfailure() { + return failable<V, string>(false, string(), -1); +} + +template<typename V, typename F, typename C> const lambda<failable<V, F, C>(const V)> failure() { + return mkfailure<V, F, C>; +} + +/** + * Convert a failable of a given type to a failable of another type. + */ +template<typename V, typename F, typename C, typename X> const failable<V, F, C> mkfailure(const failable<X, F, C>& f, const bool log = true) { + return mkfailure<V, F, C>(reason(f), rcode(f), log); +} + +/** + * Returns true if the monad contains a content. + */ +template<typename V, typename F, typename C> const bool hasContent(const failable<V, F, C>& m) { + return m.hasv; +} + +/** + * Returns the content of a failable monad. + */ +template<typename V, typename F, typename C> const V content(const failable<V, F, C>& m) { + return m.v; +} + +/** + * Returns the reason for failure of a failable monad. + */ +template<typename V, typename F, typename C> const F reason(const failable<V, F, C>& m) { + return m.f; +} + +/** + * Returns the reason code for failure of a failable monad. + */ +template<typename V, typename F, typename C> const C rcode(const failable<V, F, C>& m) { + return m.c; +} + +/** + * Bind a function to a failable monad. Passes the success value in the monad to the function + * if present, or does nothing if there's no value and a failure instead. + */ +template<typename R, typename FR, typename XR, typename V, typename FV, typename XV> +const failable<R, FR, XR> operator>>(const failable<V, FV, XV>& m, const lambda<failable<R, FR, XR>(const V)>& f) { + if (!hasContent(m)) + return m; + return f(content(m)); +} + +template<typename R, typename FR, typename XR, typename V, typename FV, typename XV> +const failable<R, FR, XR> operator>>(const failable<V, FV, XV>& m, const failable<R, FR, XR> (* const f)(const V)) { + if (!hasContent(m)) + return m; + return f(content(m)); +} + +/** + * State + content pair data type used by the state monad. + */ +template<typename S, typename V> class scp { +public: + scp(const S& s, const V& v) : s(s), v(v) { + } + + operator const S() const { + return s; + } + + operator const V() const { + return v; + } + + const scp<S, V>& operator=(const scp<S, V>& p) { + if(this == &p) + return *this; + s = p.s; + v = p.v; + return *this; + } + + const bool operator!=(const scp<S, V>& p) const { + return !this->operator==(p); + } + + const bool operator==(const scp<S, V>& p) const { + if (this == &p) + return true; + return s == p.s && v == p.v; + } + +private: + const S s; + const V v; + + template<typename A, typename B> friend const A scpstate(const scp<A, B>& m); + template<typename A, typename B> friend const B content(const scp<A, B>& m); +}; + +/** + * Returns the state of a state-content pair. + */ +template<typename S, typename V> const S scpstate(const scp<S, V>& m) { + return m.s; +} + +/** + * Returns the content of a state-content pair. + */ +template<typename S, typename V> const S content(const scp<S, V>& m) { + return m.v; +} + +/** + * State monad. Used to represent the combination of a state and a content. + */ +template<typename S, typename V> class state { +public: + state(const lambda<scp<S, V>(const S)>& f) : f(f) { + } + + const scp<S, V> operator()(const S& s) const { + return f(s); + } + + const state<S, V>& operator=(const state<S, V>& m) { + if(this == &m) + return *this; + f = m.f; + return *this; + } + + const bool operator!=(const state<S, V>& m) const { + return !this->operator==(m); + } + + const bool operator==(const state<S, V>& m) const { + if (this == &m) + return true; + return f == m.f; + } + +private: + const lambda<scp<S, V>(const S)> f; +}; + +/** + * Write a state monad to a stream. + */ +template<typename S, typename V> ostream& operator<<(ostream& out, const state<S, V>& m) { + const S s = m; + const V v = m; + out << '(' << s << ' ' << v << ')'; + return out; +} + +/** + * Return a state monad carrying a result content. + */ +template<typename S, typename V> struct returnState { + const V v; + returnState(const V& v) : v(v) { + } + const scp<S, V> operator()(const S& s) const { + return scp<S, V>(s, v); + } +}; + +template<typename S, typename V> const state<S, V> result(const V& v) { + return state<S, V>(returnState<S, V>(v)); +} + +/** + * Return a state monad with a transformer function. + * A transformer function takes a state and returns an scp pair carrying a content and a + * new (transformed) state. + */ +template<typename S, typename V> const state<S, V> transformer(const lambda<scp<S, V>(const S)>& f) { + return state<S, V>(f); +} + +/** + * Bind a function to a state monad. The function takes a content and returns a state + * monad carrying a return content. + */ +template<typename S, typename A, typename B> struct stateBind { + const state<S, A> st; + const lambda<state<S, B>(const A)>f; + + stateBind(const state<S, A>& st, const lambda<state<S, B>(const A)>& f) : st(st), f(f) { + } + + const scp<S, B> operator()(const S& is) const { + const scp<S, A> iscp = st(is); + const state<S, B> m = f((A)iscp); + return m((S)iscp); + } +}; + +template<typename S, typename A, typename B> +const state<S, B> operator>>(const state<S, A>& st, const lambda<state<S, B>(const A)>& f) { + return state<S, B>(stateBind<S, A , B>(st, f)); +} + +template<typename S, typename A, typename B> +const state<S, B> operator>>(const state<S, A>& st, const state<S, B> (* const f)(const A)) { + return state<S, B>(stateBind<S, A , B>(st, f)); +} + +} +#endif /* tuscany_monad_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/parallel-test.cpp b/sca-cpp/branches/lightweight-sca/kernel/parallel-test.cpp new file mode 100644 index 0000000000..28e484d42b --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/parallel-test.cpp @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test parallel functions. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "function.hpp" +#include "list.hpp" +#include "perf.hpp" +#include "parallel.hpp" + +namespace tuscany { + +int inci = 0; + +struct incPerf { + incPerf() { + } + const bool operator()() const { + inci = inci + 1; + return true; + } +}; + +const gc_ptr<int> tlsic() { + gc_ptr<int> i = new (gc_new<int>()) int(); + *i = 0; + return i; +} +const perthread_ptr<int> tlsi(tlsic); + +struct tlsPerf { + tlsPerf() { + } + const bool operator()() const { + *tlsi = *tlsi + 1; + return true; + } +}; + +#ifdef WANT_THREADS + +int addi = 0; + +struct addAndFetchPerf { + addAndFetchPerf() { + } + const bool operator()() const { + __sync_add_and_fetch(&addi, 1); + return true; + } +}; + +int muxi = 0; + +struct mutexPerf { + pthread_mutex_t* mutex; + mutexPerf(pthread_mutex_t* mutex) : mutex(mutex) { + } + const bool operator()() const { + pthread_mutex_lock(mutex); + muxi = muxi + 1; + pthread_mutex_unlock(mutex); + return true; + } +}; + +#endif + +bool testAtomicPerf() { + const int count = 100000; + { + const lambda<bool()> l = incPerf(); + cout << "Non-atomic inc test " << time(l, 1000, count) << " ms" << endl; + assert(inci == count + 1000); + } +#ifdef WANT_THREADS + { + const lambda<bool()> l = addAndFetchPerf(); + cout << "Atomic inc test " << time(l, 1000, count) << " ms" << endl; + assert(addi == count + 1000); + } + { + pthread_mutex_t mutex; + pthread_mutex_init(&mutex, NULL); + const lambda<bool()> l = mutexPerf(&mutex); + cout << "Locked inc test " << time(l, 1000, count) << " ms" << endl; + assert(muxi == count + 1000); + pthread_mutex_destroy(&mutex); + } +#endif + { + const lambda<bool()> l = tlsPerf(); + cout << "Thread local inc test " << time(l, 1000, count) << " ms" << endl; + assert(*tlsi == count + 1000); + } + return true; +} + +#ifdef WANT_THREADS + +const int mtsquare(const int x) { + for(int i = 0; i < 10000000; i++) + ; + return x * x; +} + +const list<future<int> > submitSquares(worker& w, const int max, const int i) { + if (i == max) + return list<future<int> >(); + const lambda<int()> func = curry(lambda<int(const int)> (mtsquare), i); + return cons(submit(w, func), submitSquares(w, max, i + 1)); +} + +bool checkSquareResults(const list<future<int> > r, int i) { + if (isNil(r)) + return true; + assert(car(r) == i * i); + checkSquareResults(cdr(r), i + 1); + return true; +} + +const gc_ptr<unsigned long> tlsvc() { + gc_ptr<unsigned long> i = new (gc_new<unsigned long>()) unsigned long(); + *i = 0l; + return i; +} +const perthread_ptr<unsigned long> tlsv(tlsvc); + +const long int tlsset(gc_ptr<wqueue<bool>> wq, gc_ptr<wqueue<bool>> xq) { + const unsigned long v = *tlsv; + *tlsv = threadId(); + enqueue(*xq, true); + dequeue(*wq); + return v; +} + +const bool tlscheck(gc_ptr<wqueue<bool>> wq, gc_ptr<wqueue<bool>> xq) { + const bool r = *tlsv == threadId(); + enqueue(*xq, true); + dequeue(*wq); + return r; +} + +const bool waitForWorkers(wqueue<bool>& xq, const int n) { + if (n == 0) + return true; + dequeue(xq); + return waitForWorkers(xq, n - 1); +} + +const bool unblockWorkers(wqueue<bool>& wq, const int n) { + if (n == 0) + return true; + enqueue(wq, true); + return unblockWorkers(wq, n - 1); +} + +const list<future<long int> > submitTLSSets(worker& w, wqueue<bool>& wq, wqueue<bool>& xq, const int max, const int i) { + if (i == max) + return list<future<long int> >(); + const lambda<long int()> func = curry(lambda<long int(gc_ptr<wqueue<bool>>, gc_ptr<wqueue<bool>>)>(tlsset), (gc_ptr<wqueue<bool>>)&wq, (gc_ptr<wqueue<bool>>)&xq); + return cons(submit(w, func), submitTLSSets(w, wq, xq, max, i + 1)); +} + +bool checkTLSSets(const list<future<long int> > s) { + if (isNil(s)) + return true; + assert(car(s) == 0); + return checkTLSSets(cdr(s)); +} + +const list<future<bool> > submitTLSChecks(worker& w, wqueue<bool>& wq, wqueue<bool>& xq, const int max, const int i) { + if (i == max) + return list<future<bool> >(); + const lambda<bool()> func = curry(lambda<bool(gc_ptr<wqueue<bool>>, gc_ptr<wqueue<bool>>)>(tlscheck), (gc_ptr<wqueue<bool>>)&wq, (gc_ptr<wqueue<bool>>)&xq); + return cons(submit(w, func), submitTLSChecks(w, wq, xq, max, i + 1)); +} + +bool checkTLSResults(const list<future<bool> > r) { + if (isNil(r)) + return true; + assert(car(r) == true); + return checkTLSResults(cdr(r)); +} + +bool testWorker() { + const int max = 100; + worker w(max); + { + const lambda<int()> func = curry(lambda<int(const int)> (mtsquare), 2); + assert(submit(w, func) == 4); + } + { + const list<future<int> > r(submitSquares(w, max, 0)); + checkSquareResults(r, 0); + } + { + wqueue<bool> wq(max); + unblockWorkers(wq, max); + waitForWorkers(wq, max); + unblockWorkers(wq, max); + waitForWorkers(wq, max); + } + { + wqueue<bool> wq(max); + wqueue<bool> xq(max); + const list<future<long int> > s(submitTLSSets(w, wq, xq, max, 0)); + waitForWorkers(xq, max); + unblockWorkers(wq, max); + checkTLSSets(s); + const list<future<bool> > r(submitTLSChecks(w, wq, xq, max, 0)); + waitForWorkers(xq, max); + unblockWorkers(wq, max); + checkTLSResults(r); + } + shutdown(w); + return true; +} + +#endif + +} + +int main() { + tuscany::gc_scoped_pool p; + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::testAtomicPerf(); +#ifdef WANT_THREADS + tuscany::testWorker(); +#else + tuscany::cout << "Skipped multi-thread tests" << tuscany::endl; +#endif + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/kernel/parallel.hpp b/sca-cpp/branches/lightweight-sca/kernel/parallel.hpp new file mode 100644 index 0000000000..3be4d3bc8e --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/parallel.hpp @@ -0,0 +1,466 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_parallel_hpp +#define tuscany_parallel_hpp + +/** + * Simple parallel work execution functions. + */ + +#include <unistd.h> +#ifdef WANT_THREADS +#include <pthread.h> +#endif + +#include "function.hpp" +#include "list.hpp" + +namespace tuscany { + +/** + * Returns the current process id. + */ +unsigned long processId() { + return (unsigned long)getpid(); +} + +#ifdef WANT_THREADS + +/** + * Returns the current thread id. + */ +unsigned long threadId() { + return (unsigned long)pthread_self(); +} + +/** + * Represents a value which will be know in the future. + */ +template<typename T> class future { + +private: + template<typename X> class futureValue { + public: + futureValue() : hasValue(false) { + pthread_mutex_init(&valueMutex, NULL); + pthread_cond_init(&valueCond, NULL); + } + + futureValue(const futureValue& fv) : valueMutex(fv.valueMutex), valueCond(fv.valueCond), hasValue(fv.hasValue), value(fv.value) { + } + + ~futureValue() { + //pthread_mutex_destroy(&valueMutex); + //pthread_cond_destroy(&valueCond); + } + + bool set(const T& v) { + pthread_mutex_lock(&valueMutex); + if(hasValue) { + pthread_mutex_unlock(&valueMutex); + return false; + } + hasValue = true; + value = v; + pthread_mutex_unlock(&valueMutex); + pthread_cond_broadcast(&valueCond); + return true; + } + + const T get() { + pthread_mutex_lock(&valueMutex); + while(!hasValue) { + pthread_cond_wait(&valueCond, &valueMutex); + } + const T& v = value; + pthread_mutex_unlock(&valueMutex); + return v; + } + + private: + pthread_mutex_t valueMutex; + pthread_cond_t valueCond; + bool hasValue; + X value; + }; + + gc_ptr<futureValue<T> > fvalue; + + template<typename X> friend const X get(const future<X>& f); + template<typename X> friend bool set(const future<X>& f, const X& v); + +public: + future() : fvalue(new (gc_new<futureValue<T> >()) futureValue<T>()) { + } + + ~future() { + } + + future(const future& f) : fvalue(f.fvalue) { + } + + const future& operator=(const future& f) { + if (&f == this) + return *this; + fvalue = f.fvalue; + return *this; + } + + const future& operator=(const T& v) const { + fvalue->set(v); + return *this; + } + + operator const T() const { + return fvalue->get(); + } +}; + +/** + * A bounded thread safe queue. + */ +template<typename T> class wqueue { +public: + wqueue(size_t max) : max(max), size(0), tail(0), head(0), values(new (gc_anew<T>(max)) T[max]) { + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&full, NULL); + pthread_cond_init(&empty, NULL); + } + + wqueue(const wqueue& wq) : max(wq.max), size(wq.size), tail(wq.tail), head(wq.head), mutex(wq.mutex), full(wq.full), empty(wq.empty), values(wq.values) { + } + + ~wqueue() { + //pthread_mutex_destroy(&mutex); + //pthread_cond_destroy(&full); + //pthread_cond_destroy(&empty); + } + +private: + const size_t max; + size_t size; + size_t tail; + size_t head; + pthread_mutex_t mutex; + pthread_cond_t full; + pthread_cond_t empty; + gc_ptr<T> values; + + template<typename X> friend const size_t enqueue(wqueue<X>& q, const X& v); + template<typename X> friend const X dequeue(wqueue<X>& q); +}; + +/** + * Adds an element to the tail of the queue. + */ +template<typename T> const size_t enqueue(wqueue<T>&q, const T& v) { + pthread_mutex_lock(&q.mutex); + while(q.size == q.max) + pthread_cond_wait(&q.full, &q.mutex); + q.values[q.tail] = v; + q.tail = (q.tail + 1) % q.max; + q.size++; + pthread_mutex_unlock(&q.mutex); + pthread_cond_broadcast(&q.empty); + return q.size; +} + +/** + * Returns the element at the head of the queue. + */ +template<typename T> const T dequeue(wqueue<T>& q) { + pthread_mutex_lock(&q.mutex); + while(q.size == 0) + pthread_cond_wait(&q.empty, &q.mutex); + const T v = q.values[q.head]; + q.head = (q.head + 1) % q.max; + q.size--; + pthread_mutex_unlock(&q.mutex); + pthread_cond_broadcast(&q.full); + return v; +} + +/** + * The worker thread function. + */ +void *workerThreadFunc(void *arg) { + int ost; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &ost); + int ot; + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &ot); + + wqueue<lambda<bool()> >* work = reinterpret_cast<wqueue<lambda<bool()> >*>(arg); + while(dequeue(*work)()) + ; + return NULL; +} + +/** + * Returns a list of worker threads. + */ +const list<pthread_t> workerThreads(wqueue<lambda<bool()> >& wqueue, const size_t count) { + if (count == 0) + return list<pthread_t>(); + pthread_t thread; + pthread_create(&thread, NULL, workerThreadFunc, &wqueue); + return cons(thread, workerThreads(wqueue, count - 1)); +} + +/** + * A worker, implemented with a work queue and a pool of threads. + */ +class worker { +private: + + // The worker holds a reference to a sharedWorker, to avoid non-thread-safe + // copies of the queue and thread pool when a worker is copied + class sharedWorker { + public: + sharedWorker(size_t max) : work(wqueue<lambda<bool()> >(max)), threads(workerThreads(work, max)) { + } + + wqueue<lambda<bool()> > work; + const list<pthread_t> threads; + }; + +public: + worker(size_t max) : w(*(new (gc_new<sharedWorker>()) sharedWorker(max))) { + } + + worker(const worker& wk) : w(wk.w) { + } + +private: + sharedWorker& w; + + template<typename X> friend const future<X> submit(worker& w, const lambda<X()>& func); + friend const bool shutdown(worker& w); + friend const bool cancel(worker& w); +}; + +/** + * Function used to wrap work submitted to a worker. + */ +template<typename R> bool submitFunc(const lambda<R()>& func, const future<R>& fut) { + fut = func(); + return true; +} + +/** + * Submits work to a worker. + */ +template<typename R> const future<R> submit(worker& w, const lambda<R()>& func) { + const future<R> fut; + const lambda<bool()> f = curry(lambda<bool(const lambda<R()>, future<R>)>(submitFunc<R>), func, fut); + enqueue(w.w.work, f); + return fut; +} + +/** + * Enqueues shutdown requests. + */ +const bool shutdownEnqueue(const list<pthread_t>& threads, wqueue<lambda<bool()> >& work) { + if (isNil(threads)) + return true; + enqueue(work, result(false)); + return shutdownEnqueue(cdr(threads), work); +} + +/** + * Waits for shut down threads to terminate. + */ +const bool shutdownJoin(const list<pthread_t>& threads) { + if (isNil(threads)) + return true; + pthread_join(car(threads), NULL); + return shutdownJoin(cdr(threads)); +} + +/** + * Shutdown a worker. + */ +const bool shutdown(worker& w) { + shutdownEnqueue(w.w.threads, w.w.work); + shutdownJoin(w.w.threads); + return true; +} + +/** + * Cancel a worker. + */ +const bool cancel(const list<pthread_t>& threads) { + if (isNil(threads)) + return true; + pthread_cancel(car(threads)); + return cancel(cdr(threads)); +} + +const bool cancel(worker& w) { + cancel(w.w.threads); + return true; +} + +#else + +/** + * Returns the current thread id. + */ +unsigned long threadId() { + return 0; +} + +#endif + +/** + * Represents a per-thread value. + */ +template<typename T> class perthread_ptr { +public: + perthread_ptr() : key(createkey()), owner(true), cl(lambda<gc_ptr<T>()>()), managed(false) { + } + + perthread_ptr(const lambda<gc_ptr<T>()>& cl) : key(createkey()), owner(true), cl(cl), managed(true) { + } + + ~perthread_ptr() { + if (owner) + deletekey(key); + } + + perthread_ptr(const perthread_ptr& c) : key(c.key), owner(false), cl(c.cl), managed(c.managed) { + } + + perthread_ptr& operator=(const perthread_ptr& r) throw() { + if(this == &r) + return *this; + key = r.key; + owner = false; + cl = r.cl; + managed = r.managed; + return *this; + } + + const perthread_ptr& operator=(const gc_ptr<T>& v) { + set(v); + return *this; + } + + const perthread_ptr& operator=(T* v) { + set(v); + return *this; + } + + const bool operator==(const gc_ptr<T>& r) const throw() { + return get() == r; + } + + const bool operator==(T* p) const throw() { + return get() == p; + } + + const bool operator!=(const gc_ptr<T>& r) const throw() { + return !this->operator==(r); + } + + const bool operator!=(T* p) const throw() { + return !this->operator==(p); + } + + T& operator*() const throw() { + return *get(); + } + + T* operator->() const throw() { + return get(); + } + + operator gc_ptr<T>() const { + return get(); + } + + operator T*() const { + return get(); + } + +private: +#ifdef WANT_THREADS + pthread_key_t createkey() { + pthread_key_t k; + pthread_key_create(&k, NULL); + return k; + } + + bool deletekey(pthread_key_t k) { + pthread_key_delete(k); + return true; + } + + bool set(const gc_ptr<T>& v) { + pthread_setspecific(key, (T*)v); + return true; + } + + gc_ptr<T> get() const { + const gc_ptr<T> v = static_cast<T*>(pthread_getspecific(key)); + if (v != NULL || !managed) + return v; + const gc_ptr<T> nv = cl(); + pthread_setspecific(key, nv); + return nv; + } + +#else + gc_ptr<gc_ptr<T> > createkey() { + return new (gc_new<gc_ptr<T> >()) gc_ptr<T>(); + } + + bool deletekey(unused gc_ptr<gc_ptr<T> > k) { + return true; + } + + bool set(const gc_ptr<T>& v) { + *key = v; + return true; + } + + gc_ptr<T> get() const { + if (*key != NULL || !managed) + return *key; + *key = cl(); + return *key; + } + +#endif + +#ifdef WANT_THREADS + pthread_key_t key; +#else + gc_ptr<gc_ptr<T> >key; +#endif + + bool owner; + lambda<const gc_ptr<T>()> cl; + bool managed; +}; + +} +#endif /* tuscany_parallel_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/perf.hpp b/sca-cpp/branches/lightweight-sca/kernel/perf.hpp new file mode 100644 index 0000000000..04aad06664 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/perf.hpp @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_perf_hpp +#define tuscany_perf_hpp + +/** + * Functions to help measure performance. + */ + +#include <sys/time.h> +#include <time.h> + +#include "function.hpp" + +namespace tuscany +{ + +/** + * Measure the time required to perform a function in msec. + */ +struct timeLambda { + const lambda<bool()> f; + timeLambda(const lambda<bool()>& f) : f(f) { + } + bool operator()(const long count) const { + for (long i = 0; i < count; i++) + f(); + return true; + } +}; + +const double time(const lambda<bool()>& f, const long warmup, const long count) { + const lambda<bool(long)> tl = timeLambda(f); + struct timeval start; + struct timeval end; + + tl(warmup); + gettimeofday(&start, NULL); + tl(count); + gettimeofday(&end, NULL); + + const long t = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000); + return (double)t / (double)count; +} + +const unsigned long timems() { + struct timeval t; + gettimeofday(&t, NULL); + return (unsigned long)(t.tv_sec * 1000 + t.tv_usec / 1000); +} + +const unsigned long timens() { + struct timeval t; + gettimeofday(&t, NULL); + return (unsigned long)(t.tv_sec * 1000000000 + t.tv_usec * 1000); +} + +} +#endif /* tuscany_perf_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/sstream.hpp b/sca-cpp/branches/lightweight-sca/kernel/sstream.hpp new file mode 100644 index 0000000000..17fd28b48b --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/sstream.hpp @@ -0,0 +1,262 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_sstream_hpp +#define tuscany_sstream_hpp + +/** + * Char buffer based streams. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <memory.h> +#include "string.hpp" +#include "stream.hpp" +#include "list.hpp" + +namespace tuscany { + +/** + * Instrumentable memcpy. + */ +void* stream_memcpy(void* t, const void* s, const size_t n) { + return memcpy(t, s, n); +} + +/** + * Write a list of strings into a buffer. + */ +const bool writeList(const list<string>& l, char* buf) { + if (isNil(l)) + return true; + const string c = car(l); + char* b = buf - length(c); + memcpy(b, c_str(c), length(c)); + return writeList(cdr(l), b); +} + +/** + * Output stream backed by a char buffer. + */ +class ostringstream : public ostream { +public: + ostringstream() : len(0) { + } + + ~ostringstream() { + } + + ostringstream(const ostringstream& os) { + len = os.len; + buf = os.buf; + } + + ostringstream& vprintf(const char* fmt, ...) { + va_list args; + string s; + va_start (args, fmt); + s.len = vsnprintf(NULL, 0, fmt, args); + s.buf = gc_cnew(s.len + 1); + va_start (args, fmt); + vsnprintf(s.buf, s.len + 1, fmt, args); + buf = cons(s, buf); + len += s.len; + va_end (args); + return *this; + } + + ostringstream& write(const string& s) { + buf = cons(s, buf); + len += s.len; + return *this; + } + + ostringstream& flush() { + return *this; + } + +private: + const string str() { + if (isNil(buf)) + return string(); + string s; + s.len = len; + s.buf = gc_cnew(s.len + 1); + writeList(buf, s.buf + len); + s.buf[s.len] = '\0'; + return s; + } + + friend const string str(ostringstream& os); + + size_t len; + list<string> buf; +}; + +/** + * Return a string representation of a stream. + */ +const string str(ostringstream& os) { + return os.str(); +} + +/** + * Input stream backed by a char buffer + */ +class istringstream : public istream { +public: + istringstream(const string& s) { + cur = 0; + const size_t slen = length(s); + len = slen; + buf = c_str(s); + } + + ~istringstream() { + } + + istringstream(const istringstream& is) { + len = is.len; + cur = is.cur; + buf = is.buf; + } + + const size_t read(void* b, size_t size) { + const size_t n = len - cur; + if (n == 0) + return 0; + if (n > size) { + stream_memcpy(b, buf + cur, size); + cur = cur + size; + return size; + } + stream_memcpy(b, buf + cur, n); + cur = cur + n; + return n; + } + + const bool eof() { + return cur == len; + } + + const bool fail() { + return false; + } + + const int get() { + if (eof()) + return -1; + const int c = buf[cur]; + cur += 1; + return c; + } + + const int peek() { + if (eof()) + return -1; + return buf[cur]; + } + +private: + size_t len; + size_t cur; + const char* buf; +}; + +/** + * Tokenize a string into a list of strings. + */ +const list<string> tokenize(const char* sep, const string& str) { + struct nested { + static const list<string> tokenize(const char* sep, const size_t slen, const string& str, const size_t start) { + if (start >= length(str)) + return list<string>(); + const size_t i = find(str, sep, start); + if (i == length(str)) + return mklist(string(substr(str, start))); + return cons(string(substr(str, start, i - start)), tokenize(sep, slen, str, i + slen)); + } + }; + return nested::tokenize(sep, strlen(sep), str, 0); +} + +/** + * Join a list of strings into a single string. + */ +const string join(const char* sep, const list<string>& l) { + struct nested { + static ostringstream& join(const char* sep, const list<string>& l, ostringstream& os) { + if (isNil(l)) + return os; + os << car(l); + if (!isNil(cdr(l))) + os << sep; + return join(sep, cdr(l), os); + } + }; + ostringstream os; + return str(nested::join(sep, l, os)); +} + +/** + * Returns a lazy list view of an input stream. + */ +struct ilistRead{ + istream &is; + ilistRead(istream& is) : is(is) { + } + const list<string> operator()() { + char buffer[1024]; + const size_t n = read(is, buffer, sizeof(buffer)); + if (n ==0) + return list<string>(); + return cons(string(buffer, n), (*this)()); + } +}; + +const list<string> streamList(istream& is) { + return ilistRead(is)(); +} + +/** + * Fragment the first element of a list of strings to fit the given max length. + */ +const list<string> fragment(list<string> l, size_t max) { + const string s = car(l); + if (length(s) <= max) + return l; + return cons(substr(s, 0, max), cons(substr(s, max), cdr(l))); +} + +/** + * Write a list of strings to an output stream. + */ +ostream& write(const list<string>& l, ostream& os) { + if(isNil(l)) + return os; + os << car(l); + return write(cdr(l), os); +} + +} + +#endif /* tuscany_sstream_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/stream.hpp b/sca-cpp/branches/lightweight-sca/kernel/stream.hpp new file mode 100644 index 0000000000..8ccfed948f --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/stream.hpp @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_stream_hpp +#define tuscany_stream_hpp + +/** + * Basic stream type and functions. + */ + +#include <stdarg.h> +#include "config.hpp" +#include "gc.hpp" +#include "string.hpp" + +namespace tuscany { + +/** + * Base output stream. + */ +class ostream { +public: + virtual ostream& vprintf(const char* fmt, ...) = 0; + virtual ostream& write(const string& s) = 0; + virtual ostream& flush() = 0; +}; + +/** + * Flush a stream. + */ +ostream& flush(ostream& os) { + return os.flush(); +} + +/** + * Write simple values to a stream. + */ +ostream& operator<<(ostream& os, const char* v) { + return os.vprintf("%s", v); +} + +ostream& operator<<(ostream& os, const unsigned char* v) { + return os.vprintf("%s", v); +} + +ostream& operator<<(ostream& os, const char v) { + return os.vprintf("%c", v); +} + +ostream& operator<<(ostream& os, const int v) { + return os.vprintf("%d", v); +} + +ostream& operator<<(ostream& os, const unsigned int v) { + return os.vprintf("%u", v); +} + +ostream& operator<<(ostream& os, const long int v) { + return os.vprintf("%ld", v); +} + +ostream& operator<<(ostream& os, const long unsigned int v) { + return os.vprintf("%lu", v); +} + +ostream& operator<<(ostream& os, const double v) { + return os.vprintf("%.10g", v); +} + +ostream& operator<<(ostream& os, const void* v) { + return os.vprintf("%p", v); +} + +ostream& operator<<(ostream& os, const string& v) { + return os.write(v); +} + +class stream_endl { +} endl; + +ostream& operator<<(ostream& os, unused const stream_endl e) { + os.write("\n"); + return os.flush(); +} + +/* + * Input stream. + */ +class istream { +public: + virtual const size_t read(void* buf, size_t size) = 0; + virtual const bool eof() = 0; + virtual const bool fail() = 0; + virtual const int get() = 0; + virtual const int peek() = 0; +}; + +/** + * Read from an input stream. + */ +const size_t read(istream& is, void * buf, size_t size) { + return is.read(buf, size); +} + +/** + * Return true if the end of an input stream has been reached. + */ +const bool eof(istream& is) { + return is.eof(); +} + +/** + * Return true if an input stream can't be accessed. + */ +const bool fail(istream& is) { + return is.fail(); +} + +/** + * Read a character from a stream. + */ +const int get(istream& is) { + return is.get(); +} + +/** + * Peek a character from a stream. + */ +const int peek(istream& is) { + return is.peek(); +} + +template<typename T> ostream& operator<<(ostream& out, const gc_ptr<T>& p) { + return out << p.ptr; +} + +#ifdef WANT_MAINTAINER_LOG + +/** + * Debug stream implementation with no dependencies on anything else. + */ +class odebugstream : public ostream { +public: + odebugstream() { + } + + odebugstream& vprintf(const char* fmt, ...) { + va_list args; + string s; + va_start (args, fmt); + s.len = vsnprintf(NULL, 0, fmt, args); + s.buf = gc_cnew(s.len + 1); + va_start (args, fmt); + vsnprintf(s.buf, s.len + 1, fmt, args); + buf = buf + s; + va_end (args); + return *this; + } + + odebugstream& write(const string& s) { + buf = buf + s; + return *this; + } + + odebugstream& flush() { + return *this; + } + +private: + friend const string str(odebugstream& os); + + string buf; +}; + +const string str(odebugstream& os) { + return os.buf; +} + +#endif + +} + +#endif /* tuscany_stream_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/string-test.cpp b/sca-cpp/branches/lightweight-sca/kernel/string-test.cpp new file mode 100644 index 0000000000..b6f016b294 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/string-test.cpp @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test string functions. + */ + +#include <assert.h> +#include <string> +#include "sstream.hpp" +#include "string.hpp" +#include "list.hpp" +#include "perf.hpp" + +namespace tuscany { + +bool testCopies() { + resetStringCopyCounters(); + string x("abcd"); + assert(checkStringCopyCounters(1)); + resetStringCopyCounters(); + string y = string("abcd"); + assert(checkStringCopyCounters(1)); + resetStringCopyCounters(); + unused string z = y; + assert(checkStringCopyCounters(0)); + resetStringCopyCounters(); + const list<string> pl = list<string>() + "abcd" + "efgh"; + printStringCopyCounters(); + resetStringCopyCounters(); + const list<string> cl = cons<string>("efgh", mklist<string>("abcd")); + printStringCopyCounters(); + return true; +} + +bool testString() { + const string s("abcd"); + assert(length(s) == 4); + assert(!strcmp(c_str(s), "abcd")); + + assert(s == "abcd"); + assert(s == string("abcd")); + assert(s != "zbcd"); + + assert(s < "zbcd"); + assert(s < "zbc"); + assert(s < "abzd"); + assert(s < "abcdz"); + + assert(s > "Abcd"); + assert(s > "Abc"); + assert(s > "abCd"); + assert(s > "Abcdz"); + + const string x = "abcd"; + assert(!strcmp(c_str(x), "abcd")); + + const string y = string("abcd"); + assert(!strcmp(c_str(y), "abcd")); + + assert(string("ab") + "cd" == "abcd"); + + assert(find("abcd", "cd") == 2); + assert(find("abcd", "xy") == length("abcd")); + assert(find_first_of("abcd", "cd") == 2); + assert(find_first_of("abcd", "xy") == length("abcd")); + assert(substr("abcdef", 4) == "ef"); + assert(substr("abcdef", 4, 2) == "ef"); + assert(substr("abcdef", 4, 3) == "ef"); + assert(substr("abcdef", 6, 3) == ""); + return true; +} + +bool testStream() { + ostringstream os; + os << "ab" << "cd"; + cout << str(os) << endl; + assert(str(os) == "abcd"); + + ostringstream cs; + cs << "\'"; + assert(str(cs) == "\'"); + cs << '\''; + assert(str(cs) == "\'\'"); + + istringstream is("abcd"); + char b[2]; + assert(read(is, b, 2) == 2); + assert(string("ab") == string(b, 2)); + assert(eof(is) == false); + assert(read(is, b, 2) == 2); + assert(string("cd") == string(b, 2)); + assert(eof(is) == true); + assert(read(is, b, 2) == 0); + return true; +} + +std::string stdAdd(std::string& x, std::string& y) { + return x + y; +} + +string add(string& x, string& y) { + return x + y; +} + +char charBuffer[16385]; + +struct addStrings{ + const size_t size; + addStrings(const size_t size) : size(size) { + } + bool operator()() const { + const size_t sz = size / 4; + string x(charBuffer, sz); + string y(charBuffer, sz); + assert(length(add(x, y)) == sz * 2); + return true; + } +}; + +struct addStdStrings{ + const size_t size; + addStdStrings(const size_t size) : size(size) { + } + bool operator()() const { + const size_t sz = size / 4; + std::string x(charBuffer, sz); + std::string y(charBuffer, sz); + assert(stdAdd(x, y).length() == (unsigned int)(sz * 2)); + return true; + } +}; + +bool testStringPerf() { + memset(charBuffer, 'A', 16384); + charBuffer[16384] = '\0'; + + const int count = 10000; + { + const lambda<bool()> a16 = addStrings(16); + cout << "string test " << time(a16, 5, count) << " ms" << endl; + const lambda<bool()> a32 =addStrings(32); + cout << "string test " << time(a32, 5, count) << " ms" << endl; + const lambda<bool()> a256 =addStrings(256); + cout << "string test " << time(a256, 5, count) << " ms" << endl; + const lambda<bool()> a1024 =addStrings(1024); + cout << "string test " << time(a1024, 5, count) << " ms" << endl; + const lambda<bool()> a4096 =addStrings(4096); + cout << "string test " << time(a4096, 5, count) << " ms" << endl; + } + { + const lambda<bool()> a16 =addStdStrings(16); + cout << "Std string test " << time(a16, 5, count) << " ms" << endl; + const lambda<bool()> a32 =addStdStrings(32); + cout << "Std string test " << time(a32, 5, count) << " ms" << endl; + const lambda<bool()> a256 =addStdStrings(256); + cout << "Std string test " << time(a256, 5, count) << " ms" << endl; + const lambda<bool()> a1024 =addStdStrings(1024); + cout << "Std string test " << time(a1024, 5, count) << " ms" << endl; + const lambda<bool()> a4096 =addStdStrings(4096); + cout << "Std string test " << time(a4096, 5, count) << " ms" << endl; + } + + return true; +} + +} + +int main() { + tuscany::gc_scoped_pool p; + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::testCopies(); + tuscany::testString(); + tuscany::testStream(); + tuscany::testStringPerf(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/kernel/string.hpp b/sca-cpp/branches/lightweight-sca/kernel/string.hpp new file mode 100644 index 0000000000..e726a61b84 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/string.hpp @@ -0,0 +1,305 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_string_hpp +#define tuscany_string_hpp + +/** + * Simple and fast string type backed by a char buffer + */ + +#include <assert.h> +#include <string.h> +#include <memory.h> +#include <stdio.h> +#include "gc.hpp" + +namespace tuscany { + +#ifdef WANT_MAINTAINER_COUNTERS + +/** + * Debug utilities. Counters used to track string copies. + */ +long countStringCopies = 0; + +bool resetStringCopyCounters() { + countStringCopies = 0; + return true; +} + +bool checkStringCopyCounters(long c) { + return countStringCopies == c; +} + +bool printStringCopyCounters() { + printf("countStringCopies %ld\n", countStringCopies); + return true; +} + +#else + +#define resetStringCopyCounters() +#define checkStringCopyCounters(c) true +#define printStringCopyCounters() + +#endif + +/** + * Instrumented memcpy. + */ +void* string_memcpy(void* t, const void* s, const size_t n) { +#ifdef WANT_MAINTAINER_COUNTERS + countStringCopies += 1; +#endif + return memcpy(t, s, n); +} + +char stringEmptyBuffer[1] = { '\0' }; + +/** + * String class. The maximum string size is specified as a template parameter. + */ +class string { +public: + string() : len(0) { + buf = stringEmptyBuffer; + } + + string(const char* s) { + len = strlen(s); + if (len == 0) { + buf = stringEmptyBuffer; + return; + } + buf = gc_cnew(len + 1); + string_memcpy(buf, s, len + 1); + } + + string(const char* s, const size_t n) { + len = n; + if (len == 0) { + buf = stringEmptyBuffer; + return; + } + buf = gc_cnew(len + 1); + string_memcpy(buf, s, len); + buf[len] = '\0'; + } + + string(const size_t n, const char c) { + len = n; + if (len == 0) { + buf = stringEmptyBuffer; + return; + } + buf = gc_cnew(len + 1); + memset(buf, c, n); + buf[len] = '\0'; + } + + string(const string& s) { + len = s.len; + buf = s.buf; + } + + const string& operator=(const string& s) { + if (&s == this) + return *this; + len = s.len; + buf = s.buf; + return *this; + } + + const bool operator==(const string& s) const { + if (len != s.len) + return false; + if (buf == s.buf) + return true; + return memcmp(buf, s.buf, len) == 0; + } + + const bool operator!=(const string& s) const { + return !(*this == s); + } + + const bool operator==(const char* s) const { + if (buf == s) + return true; + return strcmp(buf, s) == 0; + } + + const bool operator!=(const char* s) const { + return !(*this == s); + } + + const bool operator<(const string& s) const { + const size_t n = len < s.len? len : s.len; + const int c = memcmp(buf, s.buf, n); + if (c < 0) + return true; + if (c == 0) + return len < s.len; + return false; + } + + const bool operator>(const string& s) const { + const size_t n = len < s.len? len : s.len; + int c = memcmp(buf, s.buf, n); + if (c > 0) + return true; + if (c == 0) + return len > s.len; + return false; + } + +private: +#ifdef WANT_MAINTAINER_LOG + friend class odebugstream; +#endif + friend class ostringstream; + friend const string operator+(const string& a, const string& b); + friend const string operator+(const string& a, const char* b); + friend const size_t length(const string& s); + friend const char* c_str(const string& s); + friend const size_t find(const string& s1, const char* s2, const size_t start); + friend const string substr(const string& s, const size_t pos, const size_t n); + + size_t len; + char* buf; +}; + +/** + * Adds two strings. + */ +const string operator+(const string& a, const string& b) { + string s; + s.len = a.len + b.len; + s.buf = gc_cnew(s.len + 1); + string_memcpy(s.buf, a.buf, a.len); + string_memcpy(s.buf + a.len, b.buf, b.len); + s.buf[s.len] = '\0'; + return s; +} + +const string operator+(const string& a, const char* b) { + string s; + const size_t blen = strlen(b); + s.len = a.len + blen; + s.buf = gc_cnew(s.len + 1); + string_memcpy(s.buf, a.buf, a.len); + string_memcpy(s.buf + a.len, b, blen); + s.buf[s.len] = '\0'; + return s; +} + +/** + * Returns the length of a string. + */ +const size_t length(const string& s) { + return s.len; +} + +/** + * Returns a string as a C zero terminated string. + */ +const char* c_str(const string& s) { + return s.buf; +} + +/** + * Find the first occurrence of string s2 in s1, starting at the given position. + */ +const size_t find(const string& s1, const char* s2, const size_t start) { + if (start >= s1.len) + return s1.len; + const char *f = strstr(s1.buf + start, s2); + if (f == NULL) + return s1.len; + return f - s1.buf; +} + +const size_t find(const string& s1, const char* s2) { + return find(s1, s2, 0); +} + +/** + * Return true if string s1 contains s2. + */ +const bool contains(const string& s1, const char* s2) { + return find(s1, s2) != length(s1); +} + +/** + * Find the first occurence of any character from a string in a string. + */ +const size_t find_first_of(const string& s1, const string& s2) { + return strcspn(c_str(s1), c_str(s2)); +} + +/** + * Find the first occurence of a character in a string. + */ +const size_t find(const string& s, const char c) { + const char* cs = c_str(s); + const char* f = strchr(cs, c); + if (f == NULL) + return length(s); + return f - cs; +} + +/** + * Find the last occurence of a character in a string. + */ +const size_t find_last(const string& s, const char c) { + const char* cs = c_str(s); + const char* f = strrchr(cs, c); + if (f == NULL) + return length(s); + return f - cs; +} + +/** + * Return a substring of a string. + */ +const string substr(const string& s, const size_t pos, const size_t n) { + if (pos >= s.len) + return string(); + if (pos + n > s.len) + return string(s.buf + pos, s.len - pos); + return string(s.buf + pos, n); +} + +const string substr(const string& s, const size_t pos) { + return substr(s, pos, length(s)); +} + +/** + * Common string constants. + */ + +string trueString("true"); +string falseString("false"); +string emptyString(""); + +} + +#endif /* tuscany_string_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/tree.hpp b/sca-cpp/branches/lightweight-sca/kernel/tree.hpp new file mode 100644 index 0000000000..89a131c324 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/tree.hpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_tree_hpp +#define tuscany_tree_hpp + +/** + * Functions to work with trees. + */ + +#include "stream.hpp" +#include "string.hpp" +#include "function.hpp" +#include "list.hpp" +#include "monad.hpp" +#include "value.hpp" + +namespace tuscany { + +/** + * Make a tree from a leaf and two branches. + */ +template<typename T> const list<T> mktree(const T& e, const list<T>& left, const list<T>& right) { + return mklist<T>(e, left, right); +} + +/** + * Find a leaf with the given key in a tree. + */ +template<typename T> const list<T> assoctree(const T& k, const list<T>& tree) { + if (isNil(tree)) + return tree; + if (k == car<T>(car(tree))) + return car(tree); + if (k < car<T>(car(tree))) + return assoctree<T>(k, cadr(tree)); + return assoctree<T>(k, caddr(tree)); +} + +/** + * Construct a new tree from a leaf and a tree. + */ +template<typename T> const list<T> constree(const T& e, const list<T>& tree) { + if (isNil(tree)) + return mktree(e, list<T>(), list<T>()); + if (e == car(tree)) + return tree; + if (e < car(tree)) + return mktree<T>(car(tree), constree<T>(e, cadr(tree)), caddr(tree)); + return mktree<T>(car(tree), cadr(tree), constree<T>(e, caddr(tree))); +} + +/** + * Make a tree from an unordered list of leaves. + */ +template<typename T> const list<T> mktree(const list<T>& l) { + if (isNil(l)) + return l; + return constree(car(l), mktree(cdr(l))); +} + +/** + * Convert a tree to an ordered list of leaves. + */ +template<typename T> const list<T> flatten(const list<T>& tree) { + if (isNil(tree)) + return tree; + return append<T>(flatten<T>(cadr(tree)), cons<T>(car(tree), flatten<T>(caddr(tree)))); +} + +/** + * Sort a list. + */ +template<typename T> const list<T> sort(const list<T>& l) { + return flatten(mktree(l)); +} + +/** + * Make a balanced tree from an ordered list of leaves. + */ +template<typename T> const list<T> btreeHelper(const list<T>& elements, const size_t n) { + if (n == 0) + return cons<T>(list<T>(), elements); + const size_t leftSize = (n - 1) / 2; { + const list<T> leftResult = btreeHelper<T>(elements, leftSize); { + const list<T> leftTree = car(leftResult); + const list<T> nonLeftElements = cdr(leftResult); + const size_t rightSize = n - (leftSize + 1); { + const T thisEntry = car(nonLeftElements); + const list<T> rightResult = btreeHelper<T>(cdr(nonLeftElements), rightSize); { + const list<T> rightTree = car(rightResult); + const list<T> remainingElements = cdr(rightResult); { + return cons<T>(mktree<T>(thisEntry, leftTree, rightTree), remainingElements); + } + } + } + } + } +} + +template<typename T> const list<T> mkbtree(const list<T>& elements) { + return car(btreeHelper<T>(elements, length(elements))); +} + +} + +#endif /* tuscany_tree_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/value.hpp b/sca-cpp/branches/lightweight-sca/kernel/value.hpp new file mode 100644 index 0000000000..206fe8b32b --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/value.hpp @@ -0,0 +1,656 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_value_hpp +#define tuscany_value_hpp + +/** + * Generic value type. + */ + +#include <stdlib.h> +#include <apr_uuid.h> +#include <apr_time.h> + +#include "string.hpp" +#include "sstream.hpp" +#include "gc.hpp" +#include "function.hpp" +#include "list.hpp" +#include "monad.hpp" + +namespace tuscany +{ + +#ifdef WANT_MAINTAINER_COUNTERS + +/** + * Debug utilities. Counters used to track instances of values + */ +long int countValues = 0; +long int countEValues = 0; +long int countCValues = 0; +long int countVValues = 0; + +bool resetValueCounters() { + countValues = countEValues = countCValues = countVValues = 0; + return true; +} + +bool checkValueCounters() { + return countValues == 0; +} + +bool printValueCounters() { + cout << "countValues " << countValues << endl; + cout << "countEValues " << countEValues << endl; + cout << "countCValues " << countCValues << endl; + cout << "countVValues " << countVValues << endl; + return true; +} + +#else + +#define resetValueCounters() +#define checkValueCounters() true +#define printValueCounters() + +#endif + +#ifdef WANT_MAINTAINER_WATCH + +/** + * Debug utilities. Macro used to write the contents of a value to + * a string, easier to watch in a debugger than the value itself. + */ + +#define debug_watchValue() do { \ + this->watch = watchValue(*this); \ + } while (0) + +#else + +#define debug_watchValue() + +#endif + +class value; + +class value { +public: + + enum ValueType { + Nil, Symbol, String, List, Number, Bool, Lambda, Ptr + }; + + value() : type(value::Nil) { + debug_inc(countValues); + debug_inc(countEValues); + debug_watchValue(); + } + + value(const value& v) { + debug_inc(countValues); + debug_inc(countCValues); + type = v.type; + switch(type) { + case value::List: + lst() = v.lst(); + case value::Lambda: + func() = v.func(); + case value::Symbol: + str() = v.str(); + case value::String: + str() = v.str(); + case value::Number: + num() = v.num(); + case value::Bool: + boo() = v.boo(); + case value::Ptr: + ptr() = v.ptr(); + default: + break; + } +#ifdef WANT_MAINTAINER_WATCH + watch = v.watch; +#endif + } + + virtual ~value() { + debug_dec(countValues); + } + + value(const lambda<value(const list<value>&)>& func) : type(value::Lambda), data(vdata(func)) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + value(const string& str) : type(value::String), data(vdata(result(str))) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + value(const char* str) : type(value::Symbol), data(vdata(result(string(str)))) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + value(const list<value>& lst) : type(value::List), data(vdata(result(lst))) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + value(const list<list<value> >& l) : type(value::List), data(vdata(result(listOfValues(l)))) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + value(const double num) : type(value::Number), data(vdata(result(num))) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + value(const int num) : type(value::Number), data(vdata(result((double)num))) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + value(const bool boo) : type(value::Bool), data(vdata(result(boo))) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + value(const gc_ptr<value> ptr) : type(value::Ptr), data(vdata(result(ptr))) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + value(const failable<value>& m) : type(value::List), + data(vdata(result(hasContent(m)? mklist<value>(content(m)) : rcode(m) == 1? mklist<value>(value(), reason(m)) : mklist<value>(value(), reason(m), rcode(m))))) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + value(const maybe<value>& m) : type(value::List), + data(vdata(result(hasContent(m)? mklist<value>(content(m)) : list<value>()))) { + debug_inc(countValues); + debug_inc(countVValues); + debug_watchValue(); + } + + const value& operator=(const value& v) { + if(this == &v) + return *this; + type = v.type; + switch(type) { + case value::List: + lst() = v.lst(); + case value::Lambda: + func() = v.func(); + case value::Symbol: + str() = v.str(); + case value::String: + str() = v.str(); + case value::Number: + num() = v.num(); + case value::Bool: + boo() = v.boo(); + case value::Ptr: + ptr() = v.ptr(); + default: + break; + } +#ifdef WANT_MAINTAINER_WATCH + watch = v.watch; +#endif + return *this; + } + + const bool operator!=(const value& v) const { + return !this->operator==(v); + } + + const bool operator==(const value& v) const { + if(this == &v) + return true; + switch(type) { + case value::Nil: + return v.type == value::Nil; + case value::List: + return v.type == value::List && lst()() == v.lst()(); + case value::Lambda: + return v.type == value::Lambda && func() == v.func(); + case value::Symbol: + case value::String: + return str()() == (string)v; + case value::Number: + return num()() == (double)v; + case value::Bool: + return boo()() == (bool)v; + case value::Ptr: + return v.type == value::Ptr && ptr()() == v.ptr()(); + default: + return false; + } + } + + const bool operator<(const value& v) const { + if(this == &v) + return false; + switch(type) { + case value::List: + return v.type == value::List && lst()() < v.lst()(); + case value::Symbol: + case value::String: + return str()() < (string)v; + case value::Bool: + return boo()() < (bool)v; + case value::Number: + return num()() < (double)v; + default: + return false; + } + } + + const bool operator>(const value& v) const { + if(this == &v) + return false; + switch(type) { + case value::List: + return v.type == value::List && lst()() > v.lst()(); + case value::Symbol: + case value::String: + return str()() > (string)v; + case value::Bool: + return boo()() > (bool)v; + case value::Number: + return num()() > (double)v; + default: + return false; + } + } + + const value operator()(const list<value>& args) const { + return func()(args); + } + + operator const string() const { + switch(type) { + case value::Symbol: + case value::String: + return str()(); + case value::Number: { + ostringstream os; + os << num()(); + return tuscany::str(os); + } + case value::Bool: + return boo()()? trueString : falseString; + default: + return emptyString; + } + } + + operator const double() const { + switch(type) { + case value::Symbol: + case value::String: + return atof(c_str(str()())); + case value::Number: + return (double)num()(); + case value::Bool: + return boo()()? 1.0 : 0.0; + default: + return 0.0; + } + } + + operator const int() const { + switch(type) { + case value::Symbol: + case value::String: + return atoi(c_str(str()())); + case value::Number: + return (int)num()(); + case value::Bool: + return boo()()? 1 : 0; + default: + return 0; + } + } + + operator const bool() const { + switch(type) { + case value::Symbol: + case value::String: + return str()() == string("true"); + case value::Number: + return (int)num()() != 0; + case value::Bool: + return boo()(); + default: + return 0; + } + } + + operator const gc_ptr<value>() const { + return ptr()(); + } + + operator const list<value>() const { + return lst()(); + } + + operator const list<list<value> >() const { + return listOfListOfValues(lst()()); + } + + operator const lambda<value(const list<value>&)>() const { + return func(); + } + +private: + template<typename T> lambda<T>& vdata() const { + return *reinterpret_cast<lambda<T> *> (const_cast<lambda<char()> *> (&data)); + } + + template<typename T> const lambda<char()>& vdata(const T& v) const { + return *reinterpret_cast<const lambda<char()> *> (&v); + } + + lambda<double()>& num() const { + return vdata<double()> (); + } + + lambda<bool()>& boo() const { + return vdata<bool()> (); + } + + lambda<gc_ptr<value>()>& ptr() const { + return vdata<gc_ptr<value>()> (); + } + + lambda<string()>& str() const { + return vdata<string()> (); + } + + lambda<list<value>()>& lst() const { + return vdata<list<value>()> (); + } + + lambda<value(const list<value>&)>& func() const { + return vdata<value(const list<value>&)> (); + } + + const list<value> listOfValues(const list<list<value> >& l) const { + if (isNil(l)) + return list<value>(); + return cons<value>(car(l), listOfValues(cdr(l))); + } + + const list<list<value> > listOfListOfValues(const list<value>& l) const { + if (isNil(l)) + return list<list<value> >(); + return cons<list<value> >(list<value>(car(l)), listOfListOfValues(cdr(l))); + } + + friend ostream& operator<<(ostream&, const value&); + friend const value::ValueType type(const value& v); + +#ifdef WANT_MAINTAINER_WATCH + friend const string watchValue(const value& v); + string watch; +#endif + + ValueType type; + lambda<char()> data; +}; + +#ifdef WANT_MAINTAINER_WATCH + +/** + * Debug utility used to write the contents of a value to a string, easier + * to watch than the value itself in a debugger. + */ +const string watchValue(const value& v) { + if (v.type == value::List) + return watchList<value>(v); + odebugstream os; + os << v; + return str(os); +} + +#endif + +/** + * Write an escape string to a buffer. + */ +const char* escapestr(const char* s, char* buf) { + if (*s == '\0') { + *buf = '\0'; + return buf; + } + if (*s == '\\' || *s == '"') { + *buf = '\\'; + *(buf + 1) = *s; + return escapestr(s + 1, buf + 2); + } + *buf = *s; + return escapestr(s + 1, buf + 1); +} + +/** + * Write an escaped string value to a stream. + */ +ostream& escvwrite(const string& str, ostream& out) { + char* buf = gc_cnew(length(str) * 2 + 1); + escapestr(c_str(str), buf); + out << buf; + return out; +} + +/** + * Write a value to a stream. + */ +ostream& operator<<(ostream& out, const value& v) { + switch(v.type) { + case value::List: + return out << v.lst()(); + case value::Lambda: + return out << "lambda::" << v.func(); + case value::Symbol: + return out << v.str()(); + case value::String: + out << '\"'; + escvwrite(v.str()(), out); + return out << '\"'; + case value::Number: + return out << v.num()(); + case value::Bool: + if(v.boo()()) + return out << "true"; + else + return out << "false"; + case value::Ptr: { + const gc_ptr<value> p = v.ptr()(); + if (p == gc_ptr<value>(NULL)) + return out << "gc_ptr::null"; + return out << "gc_ptr::" << p; + } + default: + return out << "nil"; + } +} + +/** + * Returns the type of a value. + */ +const value::ValueType type(const value& v) { + return v.type; +} + +/** + * Returns true if a value is nil. + */ +const bool isNil(const value& v) { + return type(v) == value::Nil; +} + +/** + * Returns true if a value is a lambda. + */ +const bool isLambda(const value& v) { + return type(v) == value::Lambda; +} + +/** + * Returns true if a value is a string. + */ +const bool isString(const value& v) { + return type(v) == value::String; +} + +/** + * Returns true if a value is a symbol. + */ +const bool isSymbol(const value& v) { + return type(v) == value::Symbol; +} + +/** + * Returns true if a value is a list. + */ +const bool isList(const value& v) { + return type(v) == value::List; +} + +/** + * Returns true if a value is a number. + */ +const bool isNumber(const value& v) { + return type(v) == value::Number; +} + +/** + * Returns true if a value is a boolean. + */ +const bool isBool(const value& v) { + return type(v) == value::Bool; +} + +/** + * Returns true if a value is a pointer. + */ +const bool isPtr(const value& v) { + return type(v) == value::Ptr; +} + +/** + * Returns true if a value is a tagged list. + */ +const bool isTaggedList(const value& exp, value tag) { + if(isList(exp) && !isNil((list<value>)exp)) + return car((list<value>)exp) == tag; + return false; +} + +/** + * Make a list of values from a list of other things. + */ +template<typename T> const list<value> mkvalues(const list<T>& l) { + if (isNil(l)) + return list<value>(); + return cons<value>(car(l), mkvalues(cdr(l))); +} + +/** + * Convert a list of values to a list of other things. + */ +template<typename T> const list<T> convertValues(const list<value>& l) { + if (isNil(l)) + return list<T>(); + return cons<T>(car(l), convertValues<T>(cdr(l))); +} + +/** + * Convert a path string value to a list of values. + */ +const list<string> pathTokens(const char* p) { + if (p == NULL || p[0] == '\0') + return list<string>(); + if (p[0] == '/') + return tokenize("/", p + 1); + return tokenize("/", p); +} + +const list<value> pathValues(const value& p) { + return mkvalues(pathTokens(c_str(p))); +} + +/** + * Convert a path represented as a list of values to a string value. + */ +const value path(const list<value>& p) { + if (isNil(p)) + return ""; + return string("/") + car(p) + path(cdr(p)); +} + +/** + * Make a uuid value. + */ +const value mkuuid() { + apr_uuid_t id; + apr_uuid_get(&id); + char buf[APR_UUID_FORMATTED_LENGTH]; + apr_uuid_format(buf, &id); + return value(string(buf, APR_UUID_FORMATTED_LENGTH)); +} + +/** + * Make a random alphanumeric value. + */ +const int intrand() { + const apr_uint64_t now = apr_time_now(); + srand((unsigned int)(((now >> 32) ^ now) & 0xffffffff)); + return rand() & 0x0FFFF; +} + +const value mkrand() { + char buf[32]; + const char* an = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + for (int i =0; i < 32; i++) + buf[i] = an[intrand() % 62]; + return value(string(buf, 32)); +} + +} +#endif /* tuscany_value_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/xml-test.cpp b/sca-cpp/branches/lightweight-sca/kernel/xml-test.cpp new file mode 100644 index 0000000000..0523cc74a6 --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/xml-test.cpp @@ -0,0 +1,235 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * Test XML handling functions. + */ + +#include <assert.h> +#include "stream.hpp" +#include "string.hpp" +#include "list.hpp" +#include "value.hpp" +#include "element.hpp" +#include "xml.hpp" + +namespace tuscany { + +const string currencyXML = +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +"<composite xmlns=\"http://docs.oasis-open.org/ns/opencsa/sca/200912\" targetNamespace=\"http://services\" name=\"currency\">\n" +" <component name=\"CurrencyConverterWebService\">\n" +" <implementation.java class=\"services.CurrencyConverterImpl\"/>\n" +" <service name=\"CurrencyConverter\">\n" +" <binding.ws/>\n" +" </service>\n" +" </component>\n" +" <component name=\"CurrencyConverterWebService2\">\n" +" <implementation.java class=\"services.CurrencyConverterImpl2\"/>\n" +" <service name=\"CurrencyConverter2\">\n" +" <binding.atom/>\n" +" </service>\n" +" <property name=\"currency\">US</property>\n" +" </component>\n" +"</composite>\n"; + +const string customerXML = +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +"<customer>\n" +" <name>jdoe</name>\n" +" <address>\n" +" <city>san francisco</city>\n" +" <state>ca</state>\n" +" </address>\n" +" <account>\n" +" <id>1234</id>\n" +" <balance>1000</balance>\n" +" </account>\n" +" <account>\n" +" <id>6789</id>\n" +" <balance>2000</balance>\n" +" </account>\n" +" <account>\n" +" <id>4567</id>\n" +" <balance>3000</balance>\n" +" </account>\n" +"</customer>\n"; + +const string abcXML = +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +"<a>\n" +" <m>abc</m>\n" +" <m>def</m>\n" +" <m>xyz</m>\n" +" <m>tuv</m>\n" +"</a>\n"; + +const string xyzXML = +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +"<a>\n" +" <m>\n" +" <id>123</id>\n" +" <name>abc</name>\n" +" </m>\n" +" <m>\n" +" <id>234</id>\n" +" <name>def</name>\n" +" </m>\n" +" <m>\n" +" <id>345</id>\n" +" <name>xyz</name>\n" +" </m>\n" +" <m>\n" +" <id>456</id>\n" +" <name>tuv</name>\n" +" </m>\n" +"</a>\n"; + +const bool isName(const value& token) { + return isTaggedList(token, attribute) && attributeName(token) == "name"; +} + +bool testReadXML() { + { + istringstream is(customerXML); + const list<value> c = readXML(streamList(is)); + } + { + istringstream is(currencyXML); + const list<value> c = readXML(streamList(is)); + + const value composite = car(c); + assert(isTaggedList(composite, element)); + assert(elementName(composite) == "composite"); + assert(attributeValue(car(filter<value>(isName, elementChildren(composite)))) == string("currency")); + } + return true; +} + +ostream* xmlWriter(const string& s, ostream* os) { + (*os) << s; + return os; +} + +bool testWriteXML() { + { + istringstream is(customerXML); + const list<value> c = readXML(streamList(is)); + ostringstream os; + writeXML<ostream*>(xmlWriter, &os, c); + assert(str(os) == customerXML); + } + { + istringstream is(currencyXML); + const list<value> c = readXML(streamList(is)); + ostringstream os; + writeXML<ostream*>(xmlWriter, &os, c); + assert(str(os) == currencyXML); + } + return true; +} + +bool testElements() { + { + const list<value> ad = mklist<value>(mklist<value>("city", string("san francisco")), mklist<value>("state", string("ca"))); + const list<value> ac1 = mklist<value>(mklist<value>("id", string("1234")), mklist<value>("balance", 1000)); + const list<value> ac2 = mklist<value>(mklist<value>("id", string("6789")), mklist<value>("balance", 2000)); + const list<value> ac3 = mklist<value>(mklist<value>("id", string("4567")), mklist<value>("balance", 3000)); + { + const list<value> c = mklist<value>(mklist<value>("customer", mklist<value>("name", string("jdoe")), cons<value>("address", ad), mklist<value>("account", mklist<value>(ac1, ac2, ac3)))); + const list<value> e = valuesToElements(c); + const list<value> v = elementsToValues(e); + assert(v == c); + + ostringstream os; + writeXML<ostream*>(xmlWriter, &os, e); + assert(str(os) == customerXML); + } + { + const list<value> c = mklist<value>(mklist<value>("customer", mklist<value>("name", string("jdoe")), cons<value>("address", ad), cons<value>("account", ac1), cons<value>("account", ac2), cons<value>("account", ac3))); + const list<value> e = valuesToElements(c); + const list<value> v = elementsToValues(e); + + ostringstream os; + writeXML<ostream*>(xmlWriter, &os, e); + assert(str(os) == customerXML); + } + } + { + istringstream is(abcXML); + const list<value> c = readXML(streamList(is)); + const list<value> v = elementsToValues(c); + const list<value> e = valuesToElements(v); + ostringstream os; + writeXML<ostream*>(xmlWriter, &os, e); + assert(str(os) == abcXML); + } + { + istringstream is(xyzXML); + const list<value> c = readXML(streamList(is)); + const list<value> v = elementsToValues(c); + const list<value> e = valuesToElements(v); + ostringstream os; + writeXML<ostream*>(xmlWriter, &os, e); + assert(str(os) == xyzXML); + } + { + istringstream is(customerXML); + const list<value> c = readXML(streamList(is)); + const list<value> v = elementsToValues(c); + const list<value> e = valuesToElements(v); + ostringstream os; + writeXML<ostream*>(xmlWriter, &os, e); + assert(str(os) == customerXML); + } + return true; +} + +bool testValues() { + { + const list<value> l = mklist<value>(list<value>() + "ns1:echoString" + (list<value>() + "@xmlns:ns1" + string("http://ws.apache.org/axis2/services/echo")) + (list<value>() + "text" + string("Hello World!"))); + const list<value> e = valuesToElements(l); + const failable<list<string> > lx = writeXML(e); + ostringstream os; + write(content(lx), os); + istringstream is(str(os)); + const list<value> x = readXML(streamList(is)); + const list<value> v = elementsToValues(x); + assert(v == l); + } + return true; +} + +} + +int main() { + tuscany::gc_scoped_pool p; + tuscany::cout << "Testing..." << tuscany::endl; + + tuscany::testReadXML(); + tuscany::testWriteXML(); + tuscany::testElements(); + tuscany::testValues(); + + tuscany::cout << "OK" << tuscany::endl; + + return 0; +} diff --git a/sca-cpp/branches/lightweight-sca/kernel/xml.hpp b/sca-cpp/branches/lightweight-sca/kernel/xml.hpp new file mode 100644 index 0000000000..d00a2905fb --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/xml.hpp @@ -0,0 +1,412 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_xml_hpp +#define tuscany_xml_hpp + +/** + * XML read/write functions. + */ + +#include <libxml/xmlreader.h> +#include <libxml/xmlwriter.h> +#include <libxml/xmlschemas.h> +#include <libxml/globals.h> +#include "string.hpp" +#include "list.hpp" +#include "stream.hpp" +#include "value.hpp" +#include "element.hpp" +#include "monad.hpp" + +namespace tuscany { + +/** + * APR-based memory management functions. + */ + + +/** + * Initializes the libxml2 library. + */ +class XMLParser { +public: + XMLParser() { + debug("xml::XMLParser"); + xmlMemSetup(gc_pool_free, gc_pool_malloc, gc_pool_realloc, gc_pool_strdup); + xmlInitParser(); + } + + ~XMLParser() { + } +} xmlParser; + +/** + * Encapsulates a libxml2 xmlTextReader and its state. + */ +class XMLReader { +public: + enum TokenType { + None = 0, Element = 1, Attribute = 2, Text = 3, EndElement = 15, Identifier = 100, End = 101 + }; + + XMLReader(xmlTextReaderPtr xml) : xml(xml), owner(true), tokenType(None), isEmptyElement(false), hasValue(false), hasAttributes(false) { + debug("xml::XMLReader::xml"); + xmlTextReaderSetParserProp(xml, XML_PARSER_DEFAULTATTRS, 1); + xmlTextReaderSetParserProp(xml, XML_PARSER_SUBST_ENTITIES, 1); + } + + XMLReader(const XMLReader& r) : xml(r.xml), owner(false), tokenType(r.tokenType), isEmptyElement(r.isEmptyElement), hasValue(r.hasValue), hasAttributes(r.hasAttributes) { + debug("xml::XMLReader::copy"); + } + + const XMLReader& operator=(const XMLReader& r) { + debug("xml::XMLReader::operator="); + if(this == &r) + return *this; + xml = r.xml; + owner = false; + tokenType = r.tokenType; + isEmptyElement = r.isEmptyElement; + hasValue = r.hasValue; + hasAttributes = r.hasAttributes; + return *this; + } + + ~XMLReader() { + if (!owner) + return; + xmlTextReaderClose(xml); + xmlFreeTextReader(xml); + } + + /** + * Read the next XML token and return its type. + */ + int read() { + if (tokenType == End) + return tokenType; + if (tokenType == Element) { + isEmptyElement = xmlTextReaderIsEmptyElement(xml); + hasAttributes = xmlTextReaderHasAttributes(xml); + return tokenType = Identifier; + } + if (tokenType == Identifier && hasAttributes && xmlTextReaderMoveToFirstAttribute(xml) == 1) + return tokenType = Attribute; + if (tokenType == Attribute && xmlTextReaderMoveToNextAttribute(xml) == 1) + return tokenType = Attribute; + if (isEmptyElement && (tokenType == Identifier || tokenType == Attribute)) + return tokenType = EndElement; + if (!xmlTextReaderRead(xml)) + return tokenType = End; + return tokenType = xmlTextReaderNodeType(xml); + } + + operator xmlTextReaderPtr() const { + return xml; + } + +private: + xmlTextReaderPtr xml; + bool owner; + int tokenType; + bool isEmptyElement; + bool hasValue; + bool hasAttributes; +}; + +/** + * Constants used to tag XML tokens. + */ +const value endElement("<"); +const value startElement(">"); + +/** + * Read an XML identifier. + */ +const value readIdentifier(XMLReader& reader) { + const char* name = (const char*)xmlTextReaderConstName(reader); + return name; +} + +/** + * Read XML text. + */ +const value readText(XMLReader& reader) { + const char *val = (const char*)xmlTextReaderConstValue(reader); + return string(val); +} + +/** + * Read an XML attribute. + */ +const value readAttribute(XMLReader& reader) { + const char *name = (const char*)xmlTextReaderConstName(reader); + const char *val = (const char*)xmlTextReaderConstValue(reader); + return mklist<value>(attribute, name, string(val)); +} + +/** + * Read an XML token. + */ +const value readToken(XMLReader& reader) { + const int tokenType = reader.read(); + if (tokenType == XMLReader::None || tokenType == XMLReader::End) + return value(); + if (tokenType == XMLReader::Element) + return startElement; + if (tokenType == XMLReader::Identifier) + return readIdentifier(reader); + if (tokenType == XMLReader::Attribute) + return readAttribute(reader); + if (tokenType == XMLReader::Text) + return readText(reader); + if (tokenType == XMLReader::EndElement) + return endElement; + return readToken(reader); +} + +/** + * Read a list of values from XML tokens. + */ +const list<value> readList(const list<value>& listSoFar, XMLReader& reader) { + const value token = readToken(reader); + if(isNil(token) || endElement == token) + return reverse(listSoFar); + if(startElement == token) + return readList(cons<value>(readList(mklist(element), reader), listSoFar), reader); + return readList(cons(token, listSoFar), reader); +} + +/** + * Read a list of values from a libxml2 XML reader. + */ +const list<value> read(XMLReader& reader) { + value nextToken = readToken(reader); + if (startElement == nextToken) + return mklist<value>(readList(mklist(element), reader)); + return list<value>(); +} + +/** + * Context passed to the read callback function. + */ +class XMLReadContext { +public: + XMLReadContext(const list<string>& ilist) : ilist(ilist) { + } + list<string> ilist; +}; + +/** + * Callback function called by libxml2 to read XML. + */ +int readCallback(void *context, char* buffer, int len) { + XMLReadContext& rc = *static_cast<XMLReadContext*>(context); + if (isNil(rc.ilist)) + return 0; + const list<string> f(fragment(rc.ilist, len)); + const string s(car(f)); + rc.ilist = cdr(f); + memcpy(buffer, c_str(s), length(s)); + return (int)length(s); +} + +/** + * Return true if a list of strings contains an XML document. + */ +const bool isXML(const list<string>& ls) { + if (isNil(ls)) + return false; + return substr(car(ls), 0, 5) == "<?xml"; +} + +/** + * Read a list of values from a list of strings representing an XML document. + */ +const list<value> readXML(const list<string>& ilist) { + debug(ilist, "xml::readXML"); + XMLReadContext cx(ilist); + xmlTextReaderPtr xml = xmlReaderForIO(readCallback, NULL, &cx, NULL, NULL, XML_PARSE_NONET | XML_PARSE_NODICT); + if (xml == NULL) + return list<value>(); + XMLReader reader(xml); + return read(reader); +} + +/** + * Default encoding used to write XML documents. + */ +const char* encoding = "UTF-8"; + + +/** + * Write a list of XML element or attribute tokens. + */ +const list<value> expandElementValues(const value& n, const list<value>& l) { + if (isNil(l)) + return l; + return cons<value>(value(cons<value>(element, cons<value>(n, isList(car(l))? (list<value>)car(l) : mklist(car(l))))), expandElementValues(n, cdr(l))); +} + +const failable<bool> writeList(const list<value>& l, const xmlTextWriterPtr xml) { + if (isNil(l)) + return true; + + // Write an attribute + const value token(car(l)); + if (isTaggedList(token, attribute)) { + if (xmlTextWriterWriteAttribute(xml, (const xmlChar*)c_str(string(attributeName(token))), (const xmlChar*)c_str(string(attributeValue(token)))) < 0) + return mkfailure<bool>("xmlTextWriterWriteAttribute failed"); + + } else if (isTaggedList(token, element)) { + + // Write an element containing a value + if (elementHasValue(token)) { + const value v = elementValue(token); + if (isList(v)) { + + // Write an element per entry in a list of values + const list<value> e = expandElementValues(elementName(token), v); + writeList(e, xml); + + } else { + + // Write an element with a single value + if (xmlTextWriterStartElement(xml, (const xmlChar*)c_str(string(elementName(token)))) < 0) + return mkfailure<bool>("xmlTextWriterStartElement failed"); + + // Write its children + const failable<bool> w = writeList(elementChildren(token), xml); + if (!hasContent(w)) + return w; + + if (xmlTextWriterEndElement(xml) < 0) + return mkfailure<bool>("xmlTextWriterEndElement failed"); + } + } + else { + + // Write an element + if (xmlTextWriterStartElement(xml, (const xmlChar*)c_str(string(elementName(token)))) < 0) + return mkfailure<bool>("xmlTextWriterStartElement failed"); + + // Write its children + const failable<bool> w = writeList(elementChildren(token), xml); + if (!hasContent(w)) + return w; + + if (xmlTextWriterEndElement(xml) < 0) + return mkfailure<bool>("xmlTextWriterEndElement failed"); + } + } else { + + // Write XML text + if (xmlTextWriterWriteString(xml, (const xmlChar*)c_str(string(token))) < 0) + return mkfailure<bool>("xmlTextWriterWriteString failed"); + } + + // Go on + return writeList(cdr(l), xml); +} + +/** + * Write a list of values to a libxml2 XML writer. + */ +const failable<bool> write(const list<value>& l, const xmlTextWriterPtr xml, bool xmlTag) { + if (xmlTag) { + if (xmlTextWriterStartDocument(xml, NULL, encoding, NULL) < 0) + return mkfailure<bool>("xmlTextWriterStartDocument failed"); + } + + const failable<bool> w = writeList(l, xml); + if (!hasContent(w)) + return w; + + if (xmlTag) { + if (xmlTextWriterEndDocument(xml) < 0) + return mkfailure<bool>("xmlTextWriterEndDocument failed"); + } + return true; +} + +/** + * Context passed to the write callback function. + */ +template<typename R> class XMLWriteContext { +public: + XMLWriteContext(const lambda<R(const string&, const R)>& reduce, const R& accum) : reduce(reduce), accum(accum) { + } + const lambda<R(const string&, const R)> reduce; + R accum; +}; + +/** + * Callback function called by libxml2 to write XML out. + */ +template<typename R> int writeCallback(void *context, const char* buffer, int len) { + XMLWriteContext<R>& cx = *static_cast<XMLWriteContext<R>*>(context); + cx.accum = cx.reduce(string(buffer, len), cx.accum); + return len; +} + +/** + * Convert a list of values to an XML document. + */ +template<typename R> const failable<R> writeXML(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l, const bool xmlTag) { + XMLWriteContext<R> cx(reduce, initial); + xmlOutputBufferPtr out = xmlOutputBufferCreateIO(writeCallback<R>, NULL, &cx, NULL); + if (out == NULL) + return mkfailure<R>("xmlOutputBufferCreateIO failed"); + xmlTextWriterPtr xml = xmlNewTextWriter(out); + if (xml == NULL) + return mkfailure<R>("xmlNewTextWriter failed"); + xmlTextWriterSetIndent(xml, 1); + + const failable<bool> w = write(l, xml, xmlTag); + xmlFreeTextWriter(xml); + if (!hasContent(w)) { + return mkfailure<R>(w); + } + return cx.accum; +} + +template<typename R> const failable<R> writeXML(const lambda<R(const string&, const R)>& reduce, const R& initial, const list<value>& l) { + return writeXML(reduce, initial, l, true); +} + +/** + * Convert a list of values to a list of strings representing an XML document. + */ +const failable<list<string> > writeXML(const list<value>& l, const bool xmlTag) { + debug(l, "xml::writeXML"); + const failable<list<string> > ls = writeXML<list<string> >(rcons<string>, list<string>(), l, xmlTag); + if (!hasContent(ls)) + return ls; + return reverse(list<string>(content(ls))); +} + +const failable<list<string> > writeXML(const list<value>& l) { + return writeXML(l, true); +} + +} +#endif /* tuscany_xml_hpp */ diff --git a/sca-cpp/branches/lightweight-sca/kernel/xsd-test.cpp b/sca-cpp/branches/lightweight-sca/kernel/xsd-test.cpp new file mode 100644 index 0000000000..fbd2ee6dca --- /dev/null +++ b/sca-cpp/branches/lightweight-sca/kernel/xsd-test.cpp @@ -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. + */ + +/* $Rev$ $Date$ */ + +/** + * Test validation of a composite file against an SCDL schema. + */ + +#include "string.hpp" +#include "fstream.hpp" +#include <libxml/xmlreader.h> +#include <libxml/xmlschemas.h> + +namespace tuscany { + +bool printNode(xmlTextReaderPtr reader) { + const xmlChar* name = xmlTextReaderConstName(reader); + if(name == NULL) + name = (xmlChar *)"<unknown>"; + const xmlChar* value = xmlTextReaderConstValue(reader); + cerr << xmlTextReaderDepth(reader) << " " << xmlTextReaderNodeType(reader) << " " << name << " " + << xmlTextReaderIsEmptyElement(reader) << " " << xmlTextReaderHasValue(reader); + if(value == NULL) + cerr << endl; + else + cerr << value << endl; + return true; +} + +int xmlRead(void *context, char* buffer, int len) { + return (int)fread(buffer, 1, len, (FILE*)context); +} + +int xmlClose(void *context) { + fclose((FILE*)context); + return 0; +} + +bool readFile(const char*xsdfilename, const char *filename) { + cout << "Loading schema " << xsdfilename << endl; + const xmlDocPtr xsddoc = xmlReadFile(xsdfilename, NULL, XML_PARSE_NONET); + const xmlSchemaParserCtxtPtr xsdctx = xmlSchemaNewDocParserCtxt(xsddoc); + const xmlSchemaPtr xsd = xmlSchemaParse(xsdctx); + const xmlSchemaValidCtxtPtr validctx = xmlSchemaNewValidCtxt(xsd); + + cout << "Reading file " << filename << endl; + FILE* file = fopen(filename, "r"); + if (file != NULL) { + const xmlTextReaderPtr reader = xmlReaderForIO(xmlRead, xmlClose, file, filename, NULL, XML_PARSE_NONET); + xmlTextReaderSetParserProp(reader, XML_PARSER_DEFAULTATTRS, 1); + xmlTextReaderSetParserProp(reader, XML_PARSER_SUBST_ENTITIES, 1); + + if(reader != NULL) { + xmlTextReaderSchemaValidateCtxt(reader, validctx, 0); + + int rc; + while((rc = xmlTextReaderRead(reader)) == 1) { + printNode(reader); + } + if(xmlTextReaderIsValid(reader) != 1) + cout << "Could not validate document" << endl; + xmlFreeTextReader(reader); + if(rc != 0) + cout << "Could not parse document" << endl; + } else + cout << "Could not create parser" << endl; + } else + cout << "Could not open document" << endl; + + xmlSchemaFreeValidCtxt(validctx); + xmlSchemaFree(xsd); + xmlSchemaFreeParserCtxt(xsdctx); + + return true; +} + +} + +int main(int argc, char **argv) { + tuscany::cout << "Testing..." << tuscany::endl; + if(argc != 3) + return 1; + + tuscany::readFile(argv[1], argv[2]); + + xmlCleanupParser(); + + tuscany::cout << "OK" << tuscany::endl; + return 0; +} |