Author: aconway
Date: Wed Apr  2 10:56:14 2008
New Revision: 643995

URL: http://svn.apache.org/viewvc?rev=643995&view=rev
Log:

Encoding/decoding for new types: amqp_0_10::Map, amqp_0_10:UnknownType

Added:
    incubator/qpid/trunk/qpid/cpp/rubygen/0-10/typecode.rb   (with props)
    incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.cpp   (with props)
    incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.h   (with props)
    incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h   
(with props)
    incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp   (with 
props)
    incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h   (with 
props)
    incubator/qpid/trunk/qpid/cpp/src/tests/amqp_0_10/
    incubator/qpid/trunk/qpid/cpp/src/tests/amqp_0_10/Map.cpp   (with props)
Modified:
    incubator/qpid/trunk/qpid/cpp/rubygen/0-10/specification.rb
    incubator/qpid/trunk/qpid/cpp/rubygen/cppgen.rb
    incubator/qpid/trunk/qpid/cpp/src/Makefile.am
    incubator/qpid/trunk/qpid/cpp/src/qpid/Serializer.h
    incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Codec.h
    incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h
    incubator/qpid/trunk/qpid/cpp/src/tests/Makefile.am
    incubator/qpid/trunk/qpid/cpp/src/tests/serialize.cpp

Modified: incubator/qpid/trunk/qpid/cpp/rubygen/0-10/specification.rb
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/rubygen/0-10/specification.rb?rev=643995&r1=643994&r2=643995&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/rubygen/0-10/specification.rb (original)
+++ incubator/qpid/trunk/qpid/cpp/rubygen/0-10/specification.rb Wed Apr  2 
10:56:14 2008
@@ -117,6 +117,7 @@
     h_file("[EMAIL PROTECTED]/specification") {
       include "[EMAIL PROTECTED]/built_in_types"
       include "[EMAIL PROTECTED]/complex_types"
+      include "[EMAIL PROTECTED]/Map.h"
       include "<boost/call_traits.hpp>"
       include "<iosfwd>"
       genl "using boost::call_traits;"

Added: incubator/qpid/trunk/qpid/cpp/rubygen/0-10/typecode.rb
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/rubygen/0-10/typecode.rb?rev=643995&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/rubygen/0-10/typecode.rb (added)
+++ incubator/qpid/trunk/qpid/cpp/rubygen/0-10/typecode.rb Wed Apr  2 10:56:14 
2008
@@ -0,0 +1,85 @@
+#!/usr/bin/env ruby
+$: << ".."                      # Include .. in load path
+require 'cppgen'
+
+class TypeCode < CppGen
+  def initialize(outdir, amqp)
+    super(outdir, amqp)
+    @ns="qpid::[EMAIL PROTECTED]"
+    @dir="qpid/[EMAIL PROTECTED]"
+    @types = @amqp.collect_all(AmqpType).select { |t| t.code }
+
+  end
+
+  def type_for_code_h()
+    h_file("[EMAIL PROTECTED]/TypeForCode") {
+      include "[EMAIL PROTECTED]/UnknownType.h"
+      namespace(@ns) {
+        genl
+        genl "template <uint8_t Code> struct TypeForCode;"
+        genl
+        @types.each { |t|
+          genl "template <> struct TypeForCode<#{t.code}> {  typedef 
#{t.typename} type; };"
+        }
+        genl
+        genl "template <class V> typename V::result_type"
+        scope("apply_visitor(V& visitor, uint8_t code) {") {
+          scope("switch (code) {", "}") {
+            @types.each { |t|
+              genl "case #{t.code}: return visitor((#{t.typename}*)0);"
+            }
+            genl "default: return visitor((UnknownType*)0);"
+          }
+        }
+        genl
+        genl "std::string typeName(uint8_t code);"
+      }
+    }
+  end
+
+  def type_for_code_cpp()
+    cpp_file("[EMAIL PROTECTED]/TypeForCode") {
+      include "<string>"
+      include "<sstream>"
+      namespace(@ns) {
+        namespace("") { 
+          struct("Names") {
+            scope("Names() {") {
+              scope("for (int i =0; i < 256; ++i) {") {
+                genl "std::ostringstream os;"
+                genl "os << \"UnknownType<\" << i << \">\";"
+                genl "names[i] = os.str();"
+              }
+              @types.each { |t| genl "names[#{t.code}] = \"#{t.name}\";" }
+            }  
+            genl "std::string names[256];"
+          }
+          genl "Names names;"
+        }
+        genl "std::string typeName(uint8_t code) { return names.names[code]; }"
+      }}
+  end
+
+  def code_for_type_h()
+    h_file("[EMAIL PROTECTED]/CodeForType") {
+      namespace(@ns) {
+        genl
+        genl "template <class T> struct CodeForType;"
+        genl
+        @types.each { |t|
+          genl "template <> struct CodeForType<#{t.typename}> { static const 
uint8_t value=#{t.code}; };"
+        }
+        genl
+        genl "template <class T> uint8_t codeFor(const T&) { return 
CodeForType<T>::value; }"
+      }}
+  end
+  
+  def generate
+    type_for_code_h
+    type_for_code_cpp
+    code_for_type_h
+  end
+end
+
+TypeCode.new($outdir, $amqp).generate();
+

Propchange: incubator/qpid/trunk/qpid/cpp/rubygen/0-10/typecode.rb
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/trunk/qpid/cpp/rubygen/0-10/typecode.rb
------------------------------------------------------------------------------
    svn:executable = *

Modified: incubator/qpid/trunk/qpid/cpp/rubygen/cppgen.rb
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/rubygen/cppgen.rb?rev=643995&r1=643994&r2=643995&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/rubygen/cppgen.rb (original)
+++ incubator/qpid/trunk/qpid/cpp/rubygen/cppgen.rb Wed Apr  2 10:56:14 2008
@@ -57,7 +57,6 @@
   def cppsafe() CppMangle.include?(self) ? self+"_" : self; end
 
   def amqp2cpp()
-    throw 'Invalid "array".amqp2cpp' if self=="array"
     path=split(".")
     name=path.pop
     return name.typename if path.empty?
@@ -116,7 +115,7 @@
 class AmqpElement
   # convert my amqp type_ attribute to a C++ type.
   def amqp2cpp()
-    return "Array<#{ArrayTypes[name].amqp2cpp}> " if type_=="array" 
+    return "ArrayDomain<#{ArrayTypes[name].amqp2cpp}> " if type_=="array" 
     return type_.amqp2cpp
   end
 end
@@ -166,6 +165,11 @@
   include AmqpHasFields
 end
 
+class AmqpType
+  def typename() name.typename; end
+  def fixed?() fixed_width; end
+end
+
 class AmqpCommand < AmqpAction
   def base() "Command";  end
 end
@@ -281,10 +285,11 @@
     genl
     names = name.split("::")
     names.each { |n| genl "namespace #{n} {" }
+    genl "namespace {" if (names.empty?)
     genl
     yield
     genl
-    genl('}'*names.size+" // namespace "+name)
+    genl('}'*([names.size, 1].max)+" // namespace "+name)
     genl
   end
 

Modified: incubator/qpid/trunk/qpid/cpp/src/Makefile.am
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/Makefile.am?rev=643995&r1=643994&r2=643995&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/Makefile.am (original)
+++ incubator/qpid/trunk/qpid/cpp/src/Makefile.am Wed Apr  2 10:56:14 2008
@@ -113,8 +113,13 @@
   qpid/amqp_0_10/Frame.h \
   qpid/amqp_0_10/Segment.h \
   qpid/amqp_0_10/Segment.cpp \
+  qpid/amqp_0_10/SerializableString.h \
   qpid/amqp_0_10/Assembly.h \
   qpid/amqp_0_10/Assembly.cpp \
+  qpid/amqp_0_10/Map.h \
+  qpid/amqp_0_10/Map.cpp \
+  qpid/amqp_0_10/UnknownType.h \
+  qpid/amqp_0_10/UnknownType.cpp \
   qpid/Serializer.h \
   qpid/framing/AccumulatedAck.cpp \
   qpid/framing/AMQBody.cpp \

Modified: incubator/qpid/trunk/qpid/cpp/src/qpid/Serializer.h
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/Serializer.h?rev=643995&r1=643994&r2=643995&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/Serializer.h (original)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/Serializer.h Wed Apr  2 10:56:14 2008
@@ -28,6 +28,16 @@
 namespace qpid {
 namespace serialize { 
 
+/** Wrapper to pass serializer functors by reference. */
+template <class S> struct SRef {
+    S& s;
+    SRef(S& ss) : s(ss) {}
+    template <class T> typename S::result_type operator()(T& x) { return s(x); 
}
+    template <class T> typename S::result_type operator()(const T& x) { return 
s(x); }
+};
+
+template <class S> SRef<S> ref(S& s) { return SRef<S>(s); }
+
 // FIXME aconway 2008-03-03: Document.
 // Encoder/Decoder concept: add op() for primitive types, raw(),
 // op()(Iter, Iter). Note split, encode, decode.

Modified: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Codec.h
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Codec.h?rev=643995&r1=643994&r2=643995&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Codec.h (original)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Codec.h Wed Apr  2 
10:56:14 2008
@@ -46,7 +46,6 @@
  * AMQP 0-10 encoding and decoding.
  */
 struct Codec {
-
     // FIXME aconway 2008-02-29: drop this wrapper, rename to
     // IteratorEncoder, IteratorDecoder?
 
@@ -79,7 +78,7 @@
 
 
         template <class Iter> Encode& operator()(Iter begin, Iter end) {
-            std::for_each(begin, end, *this);
+            std::for_each(begin, end, serialize::ref(*this));
             return *this;
         }
 
@@ -125,7 +124,7 @@
         Decode& operator()(double& x) { return endian(x); }
 
         template <class Iter> Decode& operator()(Iter begin, Iter end) {
-            std::for_each(begin, end, *this);
+            std::for_each(begin, end, serialize::ref(*this));
             return *this;
         }
 
@@ -172,9 +171,10 @@
         Size& operator()(float x)  { size += sizeof(x); return *this; }
         Size& operator()(double x)  { size += sizeof(x); return *this; }
 
-        template <class Iter>
-        Size& operator()(const Iter& a, const Iter& b) {
-            size += (b-a)*sizeof(*a);
+        // FIXME aconway 2008-04-02: enable-if optimized (iter,iter) for
+        // iter on fixed-size type.
+        template <class Iter> Size& operator()(Iter begin, Iter end) {
+            std::for_each(begin, end, serialize::ref(*this));
             return *this;
         }
 

Added: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.cpp
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.cpp?rev=643995&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.cpp (added)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.cpp Wed Apr  2 
10:56:14 2008
@@ -0,0 +1,68 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "Map.h"
+#include <ostream>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+MapValue::MapValue() : code(codeFor(uint8_t(0))), blob(in_place<uint8_t>(0)) {}
+
+MapValue::MapValue(const MapValue& x) : code(x.code), blob(x.blob) {}
+
+bool  MapValue::operator==(const MapValue& x) const {
+    return code == x.code;      // FIXME aconway 2008-04-01: incomplete
+}
+
+struct OstreamVisitor : public MapValue::Visitor<std::ostream&> {
+    std::ostream& out;
+    OstreamVisitor(std::ostream& o) : out(o) {}
+    template <class T> std::ostream& operator()(const T& t) {
+        return out << t;
+    }
+};
+
+std::ostream& operator<<(std::ostream& o, const MapValue& m) {
+    o << typeName(m.getCode()) << ":";
+    const_cast<MapValue&>(m).apply_visitor(OstreamVisitor(o));
+    return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const Map::value_type& v) {
+    return o << v.first << "=" << v.second;
+}
+std::ostream& operator<<(std::ostream& o, const Map& map) {
+    o << "map[";
+    std::ostream_iterator<Map::value_type> i(o, " ");
+    std::copy(map.begin(), map.end(), i);
+    return o << "]";
+}
+
+uint32_t Map::contentSize() const {
+    uint32_t result=4;          // uint32_t count 
+    for (const_iterator i=begin(); i != end(); ++i) {
+        result += Codec::size(i->first);
+        result += Codec::size(i->second);
+    }
+    return result;
+}
+
+}} // namespace qpid::amqp_0_10

Propchange: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.cpp
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.h
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.h?rev=643995&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.h (added)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.h Wed Apr  2 10:56:14 
2008
@@ -0,0 +1,179 @@
+#ifndef QPID_AMQP_0_10_MAP_H
+#define QPID_AMQP_0_10_MAP_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/Exception.h"
+#include "qpid/amqp_0_10/built_in_types.h"
+#include "qpid/amqp_0_10/UnknownType.h"
+#include "qpid/amqp_0_10/CodeForType.h"
+#include "qpid/amqp_0_10/TypeForCode.h"
+#include "qpid/amqp_0_10/Codec.h"
+#include "qpid/framing/Blob.h"
+#include <map>
+#include <string>
+#include <iosfwd>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+class Map;
+
+class MapValue {
+  public:
+    struct BadTypeException : public Exception {};
+
+    template <class R> struct Visitor { typedef R result_type; };
+
+    MapValue();
+    MapValue(const MapValue& x);
+    template <class T> explicit MapValue(const T& t);
+    template <class T> MapValue& operator=(const T& t);
+
+    template <class T> T* get();
+    template <class T> const T* get() const;
+
+    template <class V> typename V::result_type apply_visitor(V&);
+    template <class V> typename V::result_type apply_visitor(const V&);
+
+    uint8_t getCode() const { return code; }
+    
+    bool operator==(const MapValue&) const;
+
+    template <class S> void serialize(S& s) { s(code); s.split(*this); }
+    template <class S> void encode(S& s) const {
+        const_cast<MapValue*>(this)->apply_visitor(s);
+    }
+    template <class S> void decode(S& s) {
+        DecodeVisitor<S> dv(blob, s);
+        qpid::amqp_0_10::apply_visitor(dv, code);
+    }
+    
+
+  private:
+    static const size_t SIZE=128 < sizeof(Vbin32) ? sizeof(Vbin32) : 128;
+    typedef framing::Blob<SIZE> Blob;
+
+    template <class V> struct VisitVisitor;
+    template <class T> struct GetVisitor;
+    template <class D> struct DecodeVisitor;
+
+    uint8_t code;
+    Blob blob;
+};
+
+class Map : public std::map<Str8, MapValue> {
+  public:
+    template <class S> void serialize(S& s) { s.split(*this); }
+    template <class S> void encode(S& s) const;
+
+    // FIXME aconway 2008-04-02: better separation for size calcultion
+    // support for static size, optimized iterator size calc.
+    void encode(Codec::Size& s) const  { s.raw(0, contentSize() + 4/*size*/); }
+
+    template <class S> void decode(S& s);
+    
+  private:
+    uint32_t contentSize() const;
+};
+
+std::ostream& operator<<(std::ostream&, const MapValue&);
+std::ostream& operator<<(std::ostream&, const Map::value_type&);
+std::ostream& operator<<(std::ostream&, const Map&);
+
+using framing::in_place;
+
+template <class T> MapValue::MapValue(const T& t) : code(codeFor(t)), 
blob(in_place<t>()) {}
+
+template <class T> MapValue& MapValue::operator=(const T& t) {
+    code=codeFor(t);
+    blob=t;
+    return *this;
+}
+
+template <class V> struct MapValue::VisitVisitor {
+    typedef typename V::result_type result_type;
+    V& visitor;
+    Blob& blob;
+    VisitVisitor(V& v, Blob& b) : visitor(v), blob(b) {}
+
+    template <class T> result_type operator()(T*) {
+        return visitor(*reinterpret_cast<T*>(blob.get()));
+    }
+};
+    
+template <class V> typename V::result_type MapValue::apply_visitor(V& v) {
+    VisitVisitor<V> visitor(v, blob);
+    return qpid::amqp_0_10::apply_visitor(visitor, code);
+}
+
+template <class R> struct MapValue::GetVisitor {
+    typedef R* result_type;
+    const MapValue::Blob& blob;
+
+    GetVisitor(const MapValue::Blob& b) : blob(b) {}
+
+    R* operator()(R& r) { return &r; }
+    template <class T> R* operator()(T&) { return 0; }
+};
+    
+template <class D> struct MapValue::DecodeVisitor {
+    typedef void result_type;
+    MapValue::Blob& blob;
+    D& decoder;
+    DecodeVisitor(Blob& b, D& d) : blob(b), decoder(d) {}
+    
+    template <class T> void operator()(T*) {
+        T t;
+        decoder(t);
+        blob = t;
+    }
+};
+
+template <class T> T* MapValue::get() { return 
apply_visitor(GetVisitor<T>(blob)); }
+template <class T> const T* MapValue::get() const { return 
apply_visitor(GetVisitor<const T>()); }
+
+template <class V> typename V::result_type MapValue::apply_visitor(const V& v) 
{
+    return apply_visitor(const_cast<V&>(v));
+}
+
+template <class S> void Map::encode(S& s) const {
+    s(contentSize())(size()); // size, count
+    for (const_iterator i = begin(); i != end(); ++i)
+        s(i->first)(i->second); // key (type value)
+}
+
+template <class S> void Map::decode(S& s) {
+    uint32_t cSize, count;
+    // FIXME aconway 2008-04-02: runtime check that we consume exactly cSize.
+    s(cSize)(count);
+    for ( ; count > 0; --count) {
+        key_type k; MapValue v;
+        s(k)(v);
+        insert(value_type(k,v));
+    }
+}
+
+
+}} // namespace qpid::amqp_0_10
+
+#endif  /*!QPID_AMQP_0_10_MAP_H*/

Propchange: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/Map.h
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h?rev=643995&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h 
(added)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h Wed 
Apr  2 10:56:14 2008
@@ -0,0 +1,62 @@
+#ifndef QPID_AMQP_0_10_SERIALIZABLESTRING_H
+#define QPID_AMQP_0_10_SERIALIZABLESTRING_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+namespace qpid {
+namespace amqp_0_10 {
+
+/** Template for length-prefixed strings/arrays.
+ * Unique parameter allows creation of distinct SerializableString
+ * types with the smae T/SizeType
+ */
+template <class T, class SizeType, int Unique=0>
+struct SerializableString : public std::basic_string<T> {
+    SerializableString() {}
+    template <class U> SerializableString(const U& u) : 
std::basic_string<T>(u) {}
+    template <class I> SerializableString(const I& i, const I& j) : 
std::basic_string<T>(i,j) {}
+
+    using std::basic_string<T>::operator=;
+
+    template <class S> void serialize(S& s) { s.split(*this); }
+
+    template <class S> void encode(S& s) const {
+        s(SizeType(this->size()))(this->begin(), this->end());
+    }
+
+    template <class S> void decode(S& s) {
+        SizeType newSize;
+        s(newSize);
+        this->resize(newSize);
+        s(this->begin(), this->end());
+    }
+};
+
+// TODO aconway 2008-02-29: separate ostream ops
+template <class T, class SizeType>
+std::ostream& operator<<(std::ostream& o, const 
SerializableString<T,SizeType>& s) {
+    const std::basic_string<T> str(s);
+    return o << str.c_str();    // TODO aconway 2008-02-29: why doesn't o<<str 
work?
+}
+
+}} // namespace qpid::amqp_0_10
+
+#endif  /*!QPID_AMQP_0_10_SERIALIZABLESTRING_H*/

Propchange: 
incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp?rev=643995&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp (added)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp Wed Apr  2 
10:56:14 2008
@@ -0,0 +1,56 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "UnknownType.h"
+#include <boost/range/iterator_range.hpp>
+#include <ostream>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+UnknownType::Width UnknownType::WidthTable[16] = {
+    { 1, 0 },
+    { 2, 0 },
+    { 4, 0 },
+    { 8, 0 },
+    { 16, 0 },
+    { 32, 0 },
+    { 64, 0 },
+    { 128, 0 },
+    { 0, 1 },
+    { 0, 2 },    
+    { 0, 4 },
+    { -1, -1 },                   // Invalid
+    { 5, 0 },
+    { 9, 0 },
+    { -1, -1 },                   // Invalid    
+    { 0, 0 }
+};
+
+int UnknownType::fixed() const { return WidthTable[code>>4].fixed; }
+int UnknownType::variable() const { return WidthTable[code>>4].variable; }
+UnknownType::UnknownType(uint8_t c) : code(c) { data.resize(fixed()); }
+
+std::ostream& operator<<(std::ostream& o, const UnknownType& u) {
+    return o << boost::make_iterator_range(u.begin(), u.end()) << std::endl;
+}
+
+}} // namespace qpid::amqp_0_10
+

Propchange: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h?rev=643995&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h (added)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h Wed Apr  2 
10:56:14 2008
@@ -0,0 +1,86 @@
+#ifndef QPID_AMQP_0_10_UNKNOWNTYPE_H
+#define QPID_AMQP_0_10_UNKNOWNTYPE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include <vector>
+#include <iosfwd>
+#include <stdint.h>
+
+namespace qpid {
+namespace amqp_0_10 {
+
+/** Encode/decode an unknown type based on typecode. */
+class UnknownType {
+  public:
+    UnknownType(uint8_t code=0);
+    uint8_t getCode() const { return code; }
+    /** Size of fixed type or 0 if not fixed/0-length. -1 invalid */
+    int fixed() const;
+    /** Bytes in size tyep for variable width. -1 invalid */
+    int variable() const;
+
+    typedef std::vector<char>::const_iterator const_iterator;
+    const_iterator begin() const { return data.begin(); }
+    const_iterator end() const { return data.end(); }
+
+    template <class S> void serialize(S& s) { s.split(*this); }
+    template <class S> void encode(S& s) const;
+    template <class S> void decode(S& s);
+
+  private:
+    uint8_t code;
+    struct Width { int fixed; int variable; };
+    static Width WidthTable[16];
+
+    std::vector<char> data;
+};
+
+template <class S> void UnknownType::encode(S& s) const {
+    switch (variable()) {
+      case 0: break;
+      case 1: s(uint8_t(data.size())); break;
+      case 2: s(uint16_t(data.size())); break;
+      case 4: s(uint32_t(data.size())); break;
+    }
+    s(data.begin(), data.end());
+}
+
+template <class S> void UnknownType::decode(S& s) {
+    uint32_t s8;
+    uint32_t s16;
+    uint32_t s32;
+    switch (variable()) {
+      case 0: break;
+      case 1: s(s8); data.resize(s8); break;
+      case 2: s(s16); data.resize(s16); break;
+      case 4: s(s32); data.resize(s32); break;
+    }
+    s(data.begin(), data.end());
+}
+
+inline uint8_t codeFor(const UnknownType& u) { return u.getCode(); }
+
+std::ostream& operator<<(std::ostream&, const UnknownType&);
+
+}} // namespace qpid::amqp_0_10
+
+#endif  /*!QPID_AMQP_0_10_UNKNOWNTYPE_H*/

Propchange: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h?rev=643995&r1=643994&r2=643995&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h (original)
+++ incubator/qpid/trunk/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h Wed Apr  
2 10:56:14 2008
@@ -22,14 +22,16 @@
  */
 
 #include "Decimal.h"
+#include "SerializableString.h"
 #include "qpid/framing/SequenceNumber.h"
 #include "qpid/framing/Uuid.h"
 #include "qpid/sys/Time.h"
 #include <boost/array.hpp>
-#include <stdint.h>
+#include <boost/range/iterator_range.hpp>
 #include <string>
 #include <ostream>
 #include <vector>
+#include <stdint.h>
 
 /[EMAIL PROTECTED] Mapping from built-in AMQP types to C++ types */
 
@@ -37,26 +39,47 @@
 namespace amqp_0_10 {
 
 // Fixed size types
-typedef void Void;
+struct EmptyType { template <class S> void serialize(S&) {} };
+inline std::ostream& operator<<(std::ostream& o, const EmptyType&) { return o; 
} 
+
+struct Void : public EmptyType {};
+struct  Bit : public EmptyType {};
 
-typedef bool Bit;
 typedef bool Boolean;
 typedef char Char;
 typedef int8_t Int8;
 typedef int16_t Int16;
 typedef int32_t Int32;
 typedef int64_t Int64;
-typedef uint8_t Bin8;
 typedef uint8_t Uint8;
 typedef uint16_t Uint16;
-typedef uint32_t CharUtf32 ;
 typedef uint32_t Uint32;
 typedef uint64_t Uint64;
 
+// A struct to be distinct from the other 32 bit integrals.
+struct CharUtf32 {
+    uint32_t value;
+    CharUtf32(uint32_t n=0) : value(n) {}
+    operator uint32_t&() { return value; }
+    operator const uint32_t&() const { return value; }
+    template <class S> void serialize(S& s) { s(value); }
+};
+
 template <size_t N> struct Bin : public boost::array<char, N> {
     template <class S> void serialize(S& s) { s.raw(this->begin(), 
this->size()); }
 };
-        
+
+template <size_t N> std::ostream& operator<<(std::ostream& o, const Bin<N>& b) 
{
+    return o << boost::make_iterator_range(b.begin(), b.end());
+}
+
+template <> struct Bin<1> : public boost::array<char, 1> {
+    Bin(char c=0) { this->front() = c; }
+    operator char() { return this->front(); }
+    template <class S> void serialize(S& s) { s.raw(data(), size()); }
+};
+
+typedef Bin<1> Bin8;
 typedef Bin<128> Bin1024; 
 typedef Bin<16> Bin128;
 typedef Bin<2> Bin16;
@@ -76,58 +99,37 @@
 typedef Decimal<Uint8, Int32> Dec32;
 typedef Decimal<Uint8, Int64> Dec64;
 
-
-/** Template for length-prefixed strings/arrays. */
-template <class T, class SizeType>
-struct SerializableString : public std::basic_string<T> {
-    using std::basic_string<T>::operator=;
-
-    template <class S> void serialize(S& s) { s.split(*this); }
-
-    template <class S> void encode(S& s) const {
-        s(SizeType(this->size()))(this->begin(), this->end());
-    }
-
-    template <class S> void decode(S& s) {
-        SizeType newSize;
-        s(newSize);
-        this->resize(newSize);
-        s(this->begin(), this->end());
-    }
-};
-
-// TODO aconway 2008-02-29: separate ostream ops
-template <class T, class SizeType>
-std::ostream& operator<<(std::ostream& o, const 
SerializableString<T,SizeType>& s) {
-    const std::basic_string<T> str(s);
-    return o << str.c_str();    // TODO aconway 2008-02-29: why doesn't o<<str 
work?
-}
-
 // Variable width types
+
 typedef SerializableString<Uint8, Uint8> Vbin8;
-typedef SerializableString<char, Uint8> Str8Latin;
+typedef SerializableString<char, Uint8, 1> Str8Latin;
 typedef SerializableString<char, Uint8> Str8;
 typedef SerializableString<Uint16, Uint8> Str8Utf16;
 
 typedef SerializableString<Uint8, Uint16> Vbin16;
-typedef SerializableString<char, Uint16> Str16Latin;
+typedef SerializableString<char, Uint16, 1> Str16Latin;
 typedef SerializableString<char, Uint16> Str16;
 typedef SerializableString<Uint16, Uint16> Str16Utf16;
 
 typedef SerializableString<Uint8, Uint32> Vbin32;
 
+// Forward declare class types.
+class Map;
+
 // FIXME aconway 2008-02-26: Unimplemented types:
-template <class T> struct Array : public std::vector<T>  { template <class S> 
void serialize(S&) {} };
+template <class T> struct  ArrayDomain : public std::vector<T>  {
+    template <class S> void serialize(S&) {}
+};
+struct Array { template <class S> void serialize(S&) {} };
 struct ByteRanges { template <class S> void serialize(S&) {} };
 struct SequenceSet  { template <class S> void serialize(S&) {} };
-struct Map  { template <class S> void serialize(S&) {} };
 struct List  { template <class S> void serialize(S&) {} };
 struct Struct32  { template <class S> void serialize(S&) {} };
 
 // FIXME aconway 2008-03-10: dummy ostream operators
-template <class T> std::ostream& operator<<(std::ostream& o, const Array<T>&) 
{ return o; }
+template <class T> std::ostream& operator<<(std::ostream& o, const 
ArrayDomain<T>&) { return o; }
+inline std::ostream& operator<<(std::ostream& o, const Array&) { return o; }
 inline std::ostream& operator<<(std::ostream& o, const ByteRanges&) { return 
o; }
-inline std::ostream& operator<<(std::ostream& o, const Map&) { return o; }
 inline std::ostream& operator<<(std::ostream& o, const SequenceSet&) { return 
o; }
 inline std::ostream& operator<<(std::ostream& o, const List&) { return o; }
 inline std::ostream& operator<<(std::ostream& o, const Struct32&) { return o; }

Modified: incubator/qpid/trunk/qpid/cpp/src/tests/Makefile.am
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/tests/Makefile.am?rev=643995&r1=643994&r2=643995&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/tests/Makefile.am (original)
+++ incubator/qpid/trunk/qpid/cpp/src/tests/Makefile.am Wed Apr  2 10:56:14 2008
@@ -41,7 +41,8 @@
        SequenceSet.cpp \
        serialize.cpp \
        ProxyTemplate.cpp apply.cpp BoundedIterator.cpp \
-       IncompleteMessageList.cpp
+       IncompleteMessageList.cpp \
+       amqp_0_10/Map.cpp
 
 check_LTLIBRARIES += libshlibtest.la
 libshlibtest_la_LDFLAGS = -module -rpath $(abs_builddir)

Added: incubator/qpid/trunk/qpid/cpp/src/tests/amqp_0_10/Map.cpp
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/tests/amqp_0_10/Map.cpp?rev=643995&view=auto
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/tests/amqp_0_10/Map.cpp (added)
+++ incubator/qpid/trunk/qpid/cpp/src/tests/amqp_0_10/Map.cpp Wed Apr  2 
10:56:14 2008
@@ -0,0 +1,99 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "unit_test.h"
+#include "qpid/amqp_0_10/Map.h"
+#include "qpid/amqp_0_10/Codec.h"
+#include <iostream>
+
+using namespace qpid::amqp_0_10;
+using namespace std;
+
+namespace std {
+// Dummy += for back inserters so we can use them with the decoder.
+template <class C> back_insert_iterator<C>& 
operator+=(back_insert_iterator<C>& bi, size_t) { return bi; }
+}
+
+QPID_AUTO_TEST_SUITE(MapTestSuite)
+
+ BOOST_AUTO_TEST_CASE(testGetSet) {
+    MapValue v;
+    v = Str8("foo");
+    BOOST_CHECK(v.get<Str8>());
+    BOOST_CHECK(!v.get<uint8_t>());
+    BOOST_CHECK_EQUAL(*v.get<Str8>(), "foo");
+    
+    v = uint8_t(42);
+    BOOST_CHECK(!v.get<Str8>());
+    BOOST_CHECK(v.get<uint8_t>());
+    BOOST_CHECK_EQUAL(*v.get<uint8_t>(), 42);
+
+    v = uint16_t(12);
+    BOOST_CHECK(v.get<uint16_t>());
+    BOOST_CHECK_EQUAL(*v.get<uint16_t>(), 12);
+}
+
+template <class R> struct TestVisitor : public MapValue::Visitor<R> {
+    template <class T> R operator()(const T&) const { throw 
MapValue::BadTypeException(); }
+    R operator()(const R& r) const { return r; }
+};
+    
+BOOST_AUTO_TEST_CASE(testVisit) {
+    MapValue v;
+    v = Str8("foo");
+    BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Str8>()), "foo");
+    v = Uint16(42);
+    BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Uint16>()), 42);
+    try {
+        v.apply_visitor(TestVisitor<bool>());
+        BOOST_FAIL("Expecting exception");
+    }
+    catch(const MapValue::BadTypeException&) {}
+}
+
+
+BOOST_AUTO_TEST_CASE(testEncodeMapValue) {
+    MapValue mv;
+    std::string data;
+    mv = Str8("hello");
+    Codec::encode(back_inserter(data))(mv);
+    BOOST_CHECK_EQUAL(data.size(), Codec::size(mv));
+    MapValue mv2;
+    Codec::decode(data.begin())(mv2);
+    BOOST_CHECK_EQUAL(mv2.getCode(), 0x85);
+    BOOST_REQUIRE(mv2.get<Str8>());
+    BOOST_CHECK_EQUAL(*mv2.get<Str8>(), "hello");
+}
+
+BOOST_AUTO_TEST_CASE(testEncode) {
+    Map map;
+    std::string data;
+    map["A"] = true;
+    map["b"] = Str8("hello");
+    Codec::encode(back_inserter(data))(map);
+    Map map2;
+    Codec::decode(data.begin())(map2);
+    BOOST_CHECK_EQUAL(map.size(), 2u);
+    BOOST_CHECK(map["A"].get<bool>());
+    BOOST_CHECK_EQUAL(*map["b"].get<Str8>(), "hello");
+}
+
+
+QPID_AUTO_TEST_SUITE_END()

Propchange: incubator/qpid/trunk/qpid/cpp/src/tests/amqp_0_10/Map.cpp
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/qpid/trunk/qpid/cpp/src/tests/amqp_0_10/Map.cpp
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: incubator/qpid/trunk/qpid/cpp/src/tests/serialize.cpp
URL: 
http://svn.apache.org/viewvc/incubator/qpid/trunk/qpid/cpp/src/tests/serialize.cpp?rev=643995&r1=643994&r2=643995&view=diff
==============================================================================
--- incubator/qpid/trunk/qpid/cpp/src/tests/serialize.cpp (original)
+++ incubator/qpid/trunk/qpid/cpp/src/tests/serialize.cpp Wed Apr  2 10:56:14 
2008
@@ -25,6 +25,7 @@
 #include "qpid/amqp_0_10/specification.h"
 #include "qpid/amqp_0_10/ControlHolder.h"
 #include "qpid/amqp_0_10/Frame.h"
+#include "qpid/amqp_0_10/Map.h"
 
 #include <boost/test/test_case_template.hpp>
 #include <boost/type_traits/is_arithmetic.hpp>
@@ -76,16 +77,13 @@
 template <class A, class B, class C> struct concat3 { typedef typename 
concat2<A, typename concat2<B, C>::type>::type type; };
 template <class A, class B, class C, class D> struct concat4 { typedef 
typename concat2<A, typename concat3<B, C, D>::type>::type type; };
 
-typedef mpl::vector<Bit, Boolean, Char, Int32, Int64, Int8, Uint16, CharUtf32, 
Uint32, Uint64, Bin8, Uint8>::type IntegralTypes;
+typedef mpl::vector<Boolean, Char, Int32, Int64, Int8, Uint16, CharUtf32, 
Uint32, Uint64, Bin8, Uint8>::type IntegralTypes;
 typedef mpl::vector<Bin1024, Bin128, Bin16, Bin256, Bin32, Bin40, Bin512, 
Bin64, Bin72>::type BinTypes;
-// FIXME aconway 2008-03-07: float encoding
 typedef mpl::vector<Double, Float>::type FloatTypes;
 typedef mpl::vector<SequenceNo, Uuid, Datetime, Dec32, Dec64> 
FixedSizeClassTypes;
-typedef mpl::vector<Vbin8, Str8Latin, Str8, Str8Utf16, Vbin16, Str16Latin, 
Str16, Str16Utf16, Vbin32> VariableSizeTypes;
+typedef mpl::vector<Map, Vbin8, Str8Latin, Str8, Str8Utf16, Vbin16, 
Str16Latin, Str16, Str16Utf16, Vbin32> VariableSizeTypes;
 
-
-// FIXME aconway 2008-03-07: float encoding
-typedef concat3<IntegralTypes, BinTypes, /*FloatTypes, */ 
FixedSizeClassTypes>::type FixedSizeTypes;
+typedef concat4<IntegralTypes, BinTypes, FloatTypes, 
FixedSizeClassTypes>::type FixedSizeTypes;
 typedef concat2<FixedSizeTypes, VariableSizeTypes>::type AllTypes;
 
 // TODO aconway 2008-02-20: should test 64 bit integrals for order also.
@@ -107,19 +105,22 @@
 
 // Assign test values to the various types.
 void testValue(bool& b) { b = true; }
+void testValue(Bit&) { }
 template <class T> typename boost::enable_if<boost::is_arithmetic<T> >::type 
testValue(T& n) { n=42; }
+void testValue(CharUtf32& c) { c = 43; }
 void testValue(long long& l) { l = 0x012345; }
 void testValue(Datetime& dt) { dt = qpid::sys::now(); }
 void testValue(Uuid& uuid) { uuid=Uuid(true); }
 template <class E, class M> void testValue(Decimal<E,M>& d) { d.exponent=2; 
d.mantissa=0x1122; }
 void testValue(SequenceNo& s) { s = 42; }
 template <size_t N> void testValue(Bin<N>& a) { a.assign(42); }
-template <class T, class S> void testValue(SerializableString<T, S>& s) {
+template <class T, class S, int Unique> void testValue(SerializableString<T, 
S, Unique>& s) {
     char msg[]="foobar";
     s.assign(msg, msg+sizeof(msg));
 }
 void testValue(Str16& s) { s = "the quick brown fox jumped over the lazy dog"; 
}
 void testValue(Str8& s) { s = "foobar"; }
+void testValue(Map& m) { m["s"] = Str8("foobar"); m["b"] = true; m["c"] = 
uint16_t(42); }
 
 //typedef mpl::vector<Str8, Str16>::type TestTypes;
 BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, AllTypes)


Reply via email to