"Fossies" - the Fresh Open Source Software Archive

Member "gnash-0.8.10/cygnal/libamf/amf.cpp" (19 Jan 2012, 39793 Bytes) of package /linux/www/old/gnash-0.8.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // amf.cpp:  AMF (Action Message Format) rpc marshalling, for Gnash.
    2 // 
    3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
    4 //   Free Software Foundation, Inc
    5 // 
    6 // This program is free software; you can redistribute it and/or modify
    7 // it under the terms of the GNU General Public License as published by
    8 // the Free Software Foundation; either version 3 of the License, or
    9 // (at your option) any later version.
   10 // 
   11 // This program is distributed in the hope that it will be useful,
   12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
   13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14 // GNU General Public License for more details.
   15 //
   16 // You should have received a copy of the GNU General Public License
   17 // along with this program; if not, write to the Free Software
   18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   19 //
   20 
   21 #include "GnashSystemNetHeaders.h"
   22 #include "log.h"
   23 #include "GnashException.h"
   24 #include "buffer.h"
   25 #include "amf.h"
   26 #include "element.h"
   27 #include "amfutf8.h"
   28 
   29 #include <boost/shared_ptr.hpp>
   30 #include <string>
   31 #include <vector>
   32 #include <map>
   33 #include <boost/cstdint.hpp>
   34 
   35 namespace cygnal
   36 {
   37 
   38 /// \define ENSUREBYTES
   39 ///
   40 /// @param from The base address to check.
   41 ///
   42 /// @param tooFar The ending address that is one byte too many.
   43 ///
   44 /// @param size The number of bytes to check for: from to tooFar.
   45 ///
   46 /// @remarks May throw an Exception
   47 #define ENSUREBYTES(from, toofar, size) { \
   48     if ( from+size >= toofar ) \
   49         throw ParserException("Premature end of AMF stream"); \
   50 }
   51 
   52 
   53 /// \brief String representations of AMF0 data types.
   54 ///
   55 /// These are used to print more intelligent debug messages.
   56 const char *amftype_str[] = {
   57     "Number",
   58     "Boolean",
   59     "String",
   60     "Object",
   61     "MovieClip",
   62     "Null",
   63     "Undefined",
   64     "Reference",
   65     "ECMAArray",
   66     "ObjectEnd",
   67     "StrictArray",
   68     "Date",
   69     "LongString",
   70     "Unsupported",
   71     "Recordset",
   72     "XMLObject",
   73     "TypedObject",
   74     "AMF3 Data"
   75 };
   76 
   77 /// \brief Create a new AMF class.
   78 /// As most of the methods in the AMF class a static, this
   79 /// is primarily only used when encoding complex objects
   80 /// where the byte count is accumulated.
   81 AMF::AMF() 
   82     : _totalsize(0)
   83 {
   84 //    GNASH_REPORT_FUNCTION;
   85 }
   86 
   87 /// Delete the alloczted AMF class
   88 AMF::~AMF()
   89 {
   90 //    GNASH_REPORT_FUNCTION;
   91 }
   92 
   93 /// \brief Swap bytes in raw data.
   94 /// This only swaps bytes if the host byte order is little endian.
   95 ///
   96 /// @param word The address of the data to byte swap.
   97 ///
   98 /// @param size The number of bytes in the data.
   99 ///
  100 /// @return A pointer to the raw data.
  101 void *
  102 swapBytes(void *word, size_t size)
  103 {
  104     union {
  105     boost::uint16_t s;
  106     struct {
  107         boost::uint8_t c0;
  108         boost::uint8_t c1;
  109     } c;
  110     } u;
  111        
  112     u.s = 1;
  113     if (u.c.c0 == 0) {
  114     // Big-endian machine: do nothing
  115     return word;
  116     }
  117 
  118     // Little-endian machine: byte-swap the word
  119 
  120     // A conveniently-typed pointer to the source data
  121     boost::uint8_t *x = static_cast<boost::uint8_t *>(word);
  122 
  123     /// Handle odd as well as even counts of bytes
  124     std::reverse(x, x+size);
  125     
  126     return word;
  127 }
  128 
  129 //
  130 // Methods for encoding data into big endian formatted raw AMF data.
  131 //
  132 
  133 /// \brief Encode a 64 bit number to it's serialized representation.
  134 ///
  135 /// @param num A double value to serialize.
  136 ///
  137 /// @return a binary AMF packet in big endian format
  138 boost::shared_ptr<Buffer>
  139 AMF::encodeNumber(double indata)
  140 {
  141 //    GNASH_REPORT_FUNCTION;
  142     double num;
  143     // Encode the data as a 64 bit, big-endian, numeric value
  144     // only one additional byte for the type
  145     boost::shared_ptr<Buffer> buf(new Buffer(AMF0_NUMBER_SIZE + 1));
  146     *buf = Element::NUMBER_AMF0;
  147     num = indata;
  148     swapBytes(&num, AMF0_NUMBER_SIZE);
  149     *buf += num;
  150     
  151     return buf;
  152 }
  153 
  154 /// \brief Encode a Boolean object to it's serialized representation.
  155 ///
  156 /// @param flag The boolean value to serialize.
  157 ///
  158 /// @return a binary AMF packet in big endian format
  159 boost::shared_ptr<Buffer>
  160 AMF::encodeBoolean(bool flag)
  161 {
  162 //    GNASH_REPORT_FUNCTION;
  163     // Encode a boolean value. 0 for false, 1 for true
  164     boost::shared_ptr<Buffer> buf(new Buffer(2));
  165     *buf = Element::BOOLEAN_AMF0; 
  166     *buf += static_cast<boost::uint8_t>(flag);
  167     
  168     return buf;
  169 }
  170 
  171 /// \brief Encode the end of an object to it's serialized representation.
  172 ///
  173 /// @return a binary AMF packet in big endian format
  174 boost::shared_ptr<Buffer>
  175 AMF::encodeObject(const cygnal::Element &data)
  176 {
  177 //    GNASH_REPORT_FUNCTION;
  178     boost::uint32_t length;
  179     length = data.propertySize();
  180     gnash::log_debug(_("Encoded data size has %d properties"), length);
  181     boost::shared_ptr<cygnal::Buffer> buf;
  182     if (length) {
  183     buf.reset(new cygnal::Buffer);
  184     } else {
  185     return buf;
  186     }
  187     
  188     *buf = Element::OBJECT_AMF0;
  189     if (data.propertySize() > 0) {
  190     std::vector<boost::shared_ptr<cygnal::Element> >::const_iterator ait;
  191     std::vector<boost::shared_ptr<cygnal::Element> > props = data.getProperties();
  192     for (ait = props.begin(); ait != props.end(); ait++) {
  193         boost::shared_ptr<cygnal::Element> el = (*(ait));
  194         boost::shared_ptr<cygnal::Buffer> item = AMF::encodeElement(el);
  195         if (item) {
  196         *buf += item;
  197         item.reset();
  198         } else {
  199         break;
  200         }
  201         //      el->dump();
  202     }
  203     }
  204 
  205     // Terminate the object
  206     *buf += '\0';
  207     *buf += '\0';
  208     *buf += TERMINATOR;
  209 
  210     return buf;
  211 }
  212 
  213 /// \brief Encode the end of an object to it's serialized representation.
  214 ///
  215 /// @return a binary AMF packet in big endian format
  216 boost::shared_ptr<Buffer>
  217 AMF::encodeObjectEnd()
  218 {
  219 //    GNASH_REPORT_FUNCTION;
  220     boost::shared_ptr<Buffer> buf(new Buffer(1));
  221     *buf += TERMINATOR;
  222 
  223     return buf;
  224 }
  225 
  226 /// \brief Encode an "Undefined" object to it's serialized representation.
  227 ///
  228 /// @return a binary AMF packet in big endian format
  229 boost::shared_ptr<Buffer>
  230 AMF::encodeUndefined()
  231 {
  232 //    GNASH_REPORT_FUNCTION;
  233     boost::shared_ptr<Buffer> buf(new Buffer(1));
  234     *buf = Element::UNDEFINED_AMF0;
  235     
  236     return buf;
  237 }
  238 
  239 /// \brief Encode a "Unsupported" object to it's serialized representation.
  240 ///
  241 /// @return a binary AMF packet in big endian format
  242 boost::shared_ptr<Buffer>
  243 AMF::encodeUnsupported()
  244 {
  245 //    GNASH_REPORT_FUNCTION;
  246     boost::shared_ptr<Buffer> buf(new Buffer(1));
  247     *buf = Element::UNSUPPORTED_AMF0;
  248     
  249     return buf;
  250 }
  251 
  252 /// \brief Encode a Date to it's serialized representation.
  253 ///
  254 /// @param data A pointer to the raw bytes that becomes the data.
  255 /// 
  256 /// @return a binary AMF packet in big endian format
  257 boost::shared_ptr<Buffer>
  258 AMF::encodeDate(const boost::uint8_t *date)
  259 {
  260 //    GNASH_REPORT_FUNCTION;
  261 //    boost::shared_ptr<Buffer> buf;
  262     boost::shared_ptr<Buffer> buf;
  263     if (date != 0) {
  264     buf.reset(new Buffer(AMF0_NUMBER_SIZE+1));
  265     *buf = Element::DATE_AMF0;
  266     double num = *(reinterpret_cast<const double*>(date));
  267     swapBytes(&num, AMF0_NUMBER_SIZE);
  268     *buf += num;
  269     }
  270     return buf;
  271 }
  272 
  273 /// \brief Encode a NULL object to it's serialized representation.
  274 ///     A NULL object is often used as a placeholder in RTMP.
  275 ///
  276 /// @return a binary AMF packet in big endian format
  277 boost::shared_ptr<Buffer>
  278 AMF::encodeNull()
  279 {
  280 //    GNASH_REPORT_FUNCTION;
  281 
  282     boost::shared_ptr<Buffer> buf(new Buffer(1));
  283     *buf = Element::NULL_AMF0;
  284     
  285     return buf;
  286 }
  287 
  288 /// \brief Encode an XML object to it's serialized representation.
  289 ///
  290 /// @param data A pointer to the raw bytes that becomes the XML data.
  291 /// 
  292 /// @param nbytes The number of bytes to serialize.
  293 ///
  294 /// @return a binary AMF packet in big endian format
  295 boost::shared_ptr<Buffer>
  296 AMF::encodeXMLObject(const boost::uint8_t * /*data */, size_t /* size */)
  297 {
  298 //    GNASH_REPORT_FUNCTION;
  299     boost::shared_ptr<Buffer> buf;
  300     gnash::log_unimpl(_("XML AMF objects not supported yet"));
  301     buf.reset();
  302     return buf;
  303 }
  304 
  305 /// \brief Encode a Typed Object to it's serialized representation.
  306 ///
  307 /// @param data A pointer to the raw bytes that becomes the data.
  308 /// 
  309 /// @param size The number of bytes to serialize.
  310 ///
  311 /// @return a binary AMF packet in big endian format
  312 boost::shared_ptr<Buffer>
  313 AMF::encodeTypedObject(const cygnal::Element &data)
  314 {
  315 //    GNASH_REPORT_FUNCTION;
  316 
  317     size_t size = 0;
  318     boost::uint32_t props;
  319     props = data.propertySize();
  320     boost::shared_ptr<cygnal::Buffer> buf;
  321     //    log_debug("Encoded data size has %d properties", props);
  322     if (props) {
  323     // Calculate the total size of the output buffer
  324     // needed to hold the encoded properties
  325     for (size_t i=0; i<data.propertySize(); i++) {
  326         size += data.getProperty(i)->getDataSize();
  327         size += data.getProperty(i)->getNameSize();
  328         size += AMF_PROP_HEADER_SIZE;
  329     }
  330     size += data.getNameSize();
  331     buf.reset(new Buffer(size+24)); // FIXME: why are we several words off ?
  332     }
  333 
  334     *buf = Element::TYPED_OBJECT_AMF0;
  335 
  336     size_t length = data.getNameSize();
  337     boost::uint16_t enclength = length;
  338     swapBytes(&enclength, 2);
  339     *buf += enclength;
  340 
  341     if (data.getName()) {
  342     std::string name = data.getName();
  343     if (name.size() > 0) {
  344         *buf += name;
  345     }
  346     }
  347     
  348     if (data.propertySize() > 0) {
  349     std::vector<boost::shared_ptr<cygnal::Element> >::const_iterator ait;
  350     std::vector<boost::shared_ptr<cygnal::Element> > props = data.getProperties();
  351     for (ait = props.begin(); ait != props.end(); ait++) {
  352         boost::shared_ptr<cygnal::Element> el = (*(ait));
  353         boost::shared_ptr<cygnal::Buffer> item = AMF::encodeElement(el);
  354         if (item) {
  355         *buf += item;
  356         item.reset();
  357         } else {
  358         break;
  359         }
  360         //      el->dump();
  361     }
  362     }
  363 
  364     // Terminate the object
  365     *buf += '\0';
  366     *buf += '\0';
  367     *buf += TERMINATOR;
  368 
  369     return buf;
  370 }
  371 
  372 /// \brief Encode a Reference to an object to it's serialized representation.
  373 ///
  374 /// @param data A pointer to the raw bytes that becomes the data.
  375 /// 
  376 /// @param size The number of bytes to serialize.
  377 ///
  378 /// @return a binary AMF packet in big endian format (header,data)
  379 boost::shared_ptr<Buffer>
  380 AMF::encodeReference(boost::uint16_t index)
  381 {
  382 //    GNASH_REPORT_FUNCTION;
  383     boost::uint16_t num = index;
  384     boost::shared_ptr<cygnal::Buffer> buf(new Buffer(3));
  385     *buf = Element::REFERENCE_AMF0;
  386     swapBytes(&num, sizeof(boost::uint16_t));
  387     *buf += num;
  388     
  389     return buf;
  390 }
  391 
  392 /// \brief Encode a Movie Clip (swf data) to it's serialized representation.
  393 ///
  394 /// @param data A pointer to the raw bytes that becomes the data.
  395 /// 
  396 /// @param size The number of bytes to serialize.
  397 ///
  398 /// @return a binary AMF packet in big endian format (header,data)
  399 boost::shared_ptr<Buffer>
  400 AMF::encodeMovieClip(const boost::uint8_t * /*data */, size_t /* size */)
  401 {
  402 //    GNASH_REPORT_FUNCTION;
  403     boost::shared_ptr<Buffer> buf;
  404     gnash::log_unimpl(_("Movie Clip AMF objects not supported yet"));
  405     
  406     return buf;
  407 }
  408 
  409 /// \brief Encode an ECMA Array to it's serialized representation.
  410 ///     An ECMA Array, also called a Mixed Array, contains any
  411 ///     AMF data type as an item in the array.
  412 ///
  413 /// @param data A pointer to the raw bytes that becomes the data.
  414 /// 
  415 /// @param size The number of bytes to serialize.
  416 ///
  417 /// @return a binary AMF packet in big endian format
  418 boost::shared_ptr<Buffer>
  419 AMF::encodeECMAArray(const cygnal::Element &data)
  420 {
  421 //    GNASH_REPORT_FUNCTION;
  422     boost::uint32_t length;
  423     bool sparse = false;
  424     //size_t counter = 0;
  425 
  426     length = data.propertySize();
  427     //    log_debug("Encoded data size has %d properties", length);
  428     boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer);
  429     if (length == 0) {
  430     // an undefined array is only 5 bytes, 1 for the type and
  431     // 4 for the length.
  432     buf.reset(new cygnal::Buffer(5));
  433     }
  434     *buf = Element::ECMA_ARRAY_AMF0;
  435     length = 0;
  436     swapBytes(&length, sizeof(boost::uint32_t));
  437     *buf += length;
  438 
  439     // At lest for red5, it seems to encode from the last item to the
  440     // first, so we do the same for now.
  441     if (data.propertySize() > 0) {
  442     boost::shared_ptr<cygnal::Buffer> item;
  443     std::vector<boost::shared_ptr<cygnal::Element> >::const_iterator ait;    
  444     std::vector<boost::shared_ptr<cygnal::Element> > props = data.getProperties();
  445     for (ait = props.begin(); ait != props.end(); ait++) {
  446         boost::shared_ptr<cygnal::Element> el = (*(ait));
  447         if (sparse) {
  448         sparse = false;
  449 //      char num[12];
  450 //      sprintf(num, "%d", counter);
  451 //      cygnal::Element elnum(num, el->to_number());
  452 //      *buf += AMF::encodeElement(elnum);
  453 //      double nodes = items;
  454         cygnal::Element ellen("length");
  455         ellen.makeNumber(data.propertySize());
  456         *buf += AMF::encodeElement(ellen);
  457         } else {
  458         item = AMF::encodeElement(el);
  459         if (item) {
  460             *buf += item;
  461             item.reset();
  462         } else {
  463             break;
  464         }
  465         }
  466     }
  467     }
  468 #if 0
  469     double count = data.propertySize();
  470     cygnal::Element ellen("length", count);
  471     boost::shared_ptr<cygnal::Buffer> buflen = ellen.encode();
  472     *buf += buflen;
  473 #endif
  474     
  475     // Terminate the object
  476     *buf += '\0';
  477     *buf += '\0';
  478     *buf += TERMINATOR;
  479 
  480     return buf;
  481 }
  482 
  483 /// \brief Encode a Long String to it's serialized representation.
  484 ///
  485 /// @param data A pointer to the raw bytes that becomes the data.
  486 /// 
  487 /// @param size The number of bytes to serialize.
  488 ///
  489 /// @return a binary AMF packet in big endian format
  490 boost::shared_ptr<Buffer>
  491 AMF::encodeLongString(const boost::uint8_t * /* data */, size_t /* size */)
  492 {
  493 //    GNASH_REPORT_FUNCTION;
  494     boost::shared_ptr<Buffer> buf;
  495     gnash::log_unimpl(_("Long String AMF objects not supported yet"));
  496     
  497     return buf;
  498 }
  499 
  500 /// \brief Encode a Record Set to it's serialized representation.
  501 ///
  502 /// @param data A pointer to the raw bytes that becomes the data.
  503 /// 
  504 /// @param size The number of bytes to serialize.
  505 ///
  506 /// @return a binary AMF packet in big endian format
  507 boost::shared_ptr<Buffer>
  508 AMF::encodeRecordSet(const boost::uint8_t * /* data */, size_t /* size */)
  509 {
  510 //    GNASH_REPORT_FUNCTION;
  511     boost::shared_ptr<Buffer> buf;
  512     gnash::log_unimpl(_("Reecord Set AMF objects not supported yet"));
  513     
  514     return buf;
  515 }
  516 
  517 /// \brief Encode a Strict Array to it's serialized represenicttation.
  518 /// A Strict Array is one where all the items are the same
  519 /// data type, commonly either a number or a string.
  520 ///
  521 /// @param data A pointer to the raw bytes that becomes the data.
  522 /// 
  523 /// @param size The number of bytes to serialize.
  524 ///
  525 /// @return a binary AMF packet in big endian format (header,data)
  526 boost::shared_ptr<Buffer>
  527 AMF::encodeStrictArray(const cygnal::Element &data)
  528 {
  529 //    GNASH_REPORT_FUNCTION;
  530     boost::uint32_t items;
  531     items = data.propertySize();
  532     //    log_debug("Encoded data size has %d properties", items);
  533     boost::shared_ptr<cygnal::Buffer> buf(new cygnal::Buffer);
  534     if (items) {
  535     buf.reset(new cygnal::Buffer);
  536     } else {
  537     // an undefined array is only 5 bytes, 1 for the type and
  538     // 4 for the length.
  539     buf->resize(5);
  540     //  buf.reset(new cygnal::Buffer(5));
  541     }
  542     *buf = Element::STRICT_ARRAY_AMF0;
  543     swapBytes(&items, sizeof(boost::uint32_t));
  544     *buf += items;
  545 
  546     if (data.propertySize() > 0) {
  547     std::vector<boost::shared_ptr<cygnal::Element> >::const_iterator ait;    
  548     std::vector<boost::shared_ptr<cygnal::Element> > props = data.getProperties();
  549     bool sparse = false;
  550     size_t counter = 0;
  551     for (ait = props.begin(); ait != props.end(); ait++) {
  552         counter++;
  553         boost::shared_ptr<cygnal::Element> el = (*(ait));
  554 #if 0
  555         // FIXME: Red5's echo tests like to turn strict array's into ecma
  556         // arrays, but we shouldn't do that in the core.
  557         // If we see an undefined data item, then switch to an ECMA
  558         // array which is more compact. At least this is what Red5 does.
  559         if (el->getType() == Element::UNDEFINED_AMF0) {
  560         if (!sparse) {
  561             gnash::log_debug(_("Encoding a strict array as an ecma array"));
  562             *buf->reference() = Element::ECMA_ARRAY_AMF0;
  563             // When returning an ECMA array for a sparsely populated
  564             // array, Red5 adds one more to the count to be 1 based,
  565             // instead of zero based.
  566             boost::uint32_t moreitems = data.propertySize() + 1;
  567             swapBytes(&moreitems, sizeof(boost::uint32_t));
  568             boost::uint8_t *ptr = buf->reference() + 1;
  569             memcpy(ptr, &moreitems, sizeof(boost::uint32_t));
  570             sparse = true;
  571         }
  572         continue;
  573         } else {
  574 #endif
  575         if (sparse) {
  576             sparse = false;
  577             std::ostringstream os;
  578             os << counter;
  579             cygnal::Element elnum(os.str().c_str(), el->to_number());
  580             *buf += AMF::encodeElement(elnum);
  581             double nodes = items;
  582             cygnal::Element ellen("length", nodes);
  583             *buf += AMF::encodeElement(ellen);
  584         } else {
  585             boost::shared_ptr<cygnal::Buffer> item = AMF::encodeElement(el);
  586             if (item) {
  587             *buf += item;
  588             item.reset();
  589             continue;
  590             } else {
  591             break;
  592             }
  593         }
  594 //      }
  595 //      el->dump();
  596     }
  597     }
  598     
  599     return buf;
  600 }
  601 
  602 /// \brief Encode a string to it's serialized representation.
  603 /// 
  604 /// @param str a string value
  605 ///
  606 /// @return a binary AMF packet in big endian format
  607 boost::shared_ptr<Buffer>
  608 AMF::encodeString(const std::string &str)
  609 {
  610     boost::uint8_t *ptr = const_cast<boost::uint8_t *>(reinterpret_cast<const boost::uint8_t *>(str.c_str()));
  611     return encodeString(ptr, str.size());
  612 }
  613 
  614 /// \brief Encode a string to it's serialized representation.
  615 /// 
  616 /// @param data The data to serialize into big endian format
  617 /// 
  618 /// @param size The size of the data in bytes
  619 ///
  620 /// @return a binary AMF packet in big endian format
  621 boost::shared_ptr<Buffer>
  622 AMF::encodeString(boost::uint8_t *data, size_t size)
  623 {
  624 //    GNASH_REPORT_FUNCTION;
  625     boost::shared_ptr<Buffer>buf(new Buffer(size + AMF_HEADER_SIZE));
  626     *buf = Element::STRING_AMF0;
  627     // when a string is stored in an element, we add a NULL terminator so
  628     // it can be printed by to_string() efficiently. The NULL terminator
  629     // doesn't get written when encoding a string as it has a byte count
  630     // instead.
  631     boost::uint16_t length = size;
  632 //    log_debug("Encoded data size is going to be %d", length);
  633     swapBytes(&length, 2);
  634     *buf += length;
  635     buf->append(data, size);
  636     
  637     return buf;
  638 }
  639 
  640 /// \brief Encode a String object to it's serialized representation.
  641 /// A NULL String is a string with no associated data.
  642 ///
  643 /// @return a binary AMF packet in big endian format
  644 boost::shared_ptr<Buffer>
  645 AMF::encodeNullString()
  646 {
  647 //    GNASH_REPORT_FUNCTION;
  648     boost::uint16_t length;
  649     
  650     boost::shared_ptr<Buffer> buf(new Buffer(AMF_HEADER_SIZE));
  651     *buf = Element::STRING_AMF0;
  652     // when a string is stored in an element, we add a NULL terminator so
  653     // it can be printed by to_string() efficiently. The NULL terminator
  654     // doesn't get written when encoding a string as it has a byte count
  655     // instead.
  656     length = 0;
  657     *buf += length;
  658     
  659     return buf;
  660 }
  661 
  662 /// \brief Write an AMF element
  663 ///
  664 /// This encodes the data supplied to an AMF formatted one. As the
  665 /// memory is allocatd within this function, you *must* free the
  666 /// memory used for each element or you'll leak memory.
  667 ///
  668 /// A "packet" (or element) in AMF is a byte code, followed by the
  669 /// data. Sometimes the data is prefixed by a count, and sometimes
  670 /// it's terminated with a 0x09. Ya gotta love these flaky ad-hoc
  671 /// formats.
  672 ///
  673 /// All Numbers are 64 bit, big-endian (network byte order) entities.
  674 ///
  675 /// All strings are in multibyte format, which is to say, probably
  676 /// normal ASCII. It may be that these need to be converted to wide
  677 /// characters, but for now we just leave them as standard multibyte
  678 /// characters.
  679 
  680 /// \brief Encode an Element to it's serialized representation.
  681 ///
  682 /// @param el A smart pointer to the Element to encode.
  683 ///
  684 /// @return a binary AMF packet in big endian format
  685 boost::shared_ptr<Buffer>
  686 AMF::encodeElement(boost::shared_ptr<cygnal::Element> el)
  687 {
  688     return encodeElement(*el);
  689 }
  690 
  691 boost::shared_ptr<Buffer>
  692 AMF::encodeElement(const cygnal::Element& el)
  693 {
  694 //    GNASH_REPORT_FUNCTION;
  695     boost::shared_ptr<Buffer> buf;
  696     // Encode the element's data
  697     switch (el.getType()) {
  698       case Element::NOTYPE:
  699       return buf;
  700       break;
  701       case Element::NUMBER_AMF0:
  702       {
  703   //      boost::shared_ptr<Buffer> encnum = AMF::encodeNumber(el.to_number());
  704   //      *buf += encnum;
  705       buf = AMF::encodeNumber(el.to_number());
  706           break;
  707       }
  708       case Element::BOOLEAN_AMF0:
  709       {
  710 //    boost::shared_ptr<Buffer> encbool = AMF::encodeBoolean(el.to_bool());
  711 //    *buf += encodeBoolean(el.to_bool());
  712 //    *buf += encbool;
  713       buf = AMF::encodeBoolean(el.to_bool());
  714           break;
  715       }
  716       case Element::STRING_AMF0:
  717       {
  718   //      boost::shared_ptr<Buffer> encstr = AMF::encodeString(el.to_string());
  719       //      *buf += encstr;
  720       if (el.getDataSize() == 0) {
  721           buf = encodeNullString();
  722       } else {
  723           buf = encodeString(el.to_string());
  724       }
  725       break;
  726       }
  727       case Element::OBJECT_AMF0:
  728       buf = encodeObject(el);
  729           break;
  730       case Element::MOVIECLIP_AMF0:
  731       buf = encodeMovieClip(el.to_reference(), el.getDataSize());
  732           break;
  733       case Element::NULL_AMF0:
  734       //      *buf += Element::NULL_AMF0;
  735       buf = encodeNull();
  736           break;
  737       case Element::UNDEFINED_AMF0:
  738       //      *buf += Element::UNDEFINED_AMF0;
  739       buf = encodeUndefined();
  740       break;
  741       case Element::REFERENCE_AMF0:
  742       buf = encodeReference(el.to_short());
  743           break;
  744       case Element::ECMA_ARRAY_AMF0:
  745       buf = encodeECMAArray(el);
  746           break;
  747       // The Object End gets added when creating the object, so we can just ignore it here.
  748       case Element::OBJECT_END_AMF0:
  749       buf = encodeObjectEnd();
  750           break;
  751       case Element::STRICT_ARRAY_AMF0:
  752       {
  753       buf = AMF::encodeStrictArray(el);
  754           break;
  755       }
  756       case Element::DATE_AMF0:
  757       {
  758   //      boost::shared_ptr<Buffer> encdate = AMF::encodeNumber(el.to_number());
  759       buf = AMF::encodeDate(el.to_reference());
  760           break;
  761       }
  762       case Element::LONG_STRING_AMF0:
  763       buf = encodeLongString(el.to_reference(), el.getDataSize());
  764           break;
  765       case Element::UNSUPPORTED_AMF0:
  766       buf = encodeUnsupported();
  767           break;
  768       case Element::RECORD_SET_AMF0:
  769       buf = encodeRecordSet(el.to_reference(), el.getDataSize());
  770           break;
  771       case Element::XML_OBJECT_AMF0:
  772       buf = encodeXMLObject(el.to_reference(), el.getDataSize());
  773           // Encode an XML object. The data follows a 4 byte length
  774           // field. (which must be big-endian)
  775           break;
  776       case Element::TYPED_OBJECT_AMF0:
  777       buf = encodeTypedObject(el);
  778           break;
  779 //    // This is a Gnash specific value
  780 //       case Element::VARIABLE:
  781 //       case Element::FUNCTION:
  782 //          break;
  783       case Element::AMF3_DATA:
  784       gnash::log_error(_("FIXME: got AMF3 data type"));
  785       break;
  786       default:
  787       buf.reset();
  788           break;
  789     };
  790 
  791     // If the name field is set, it's a property, followed by the data
  792     boost::shared_ptr<Buffer> bigbuf;
  793     if (el.getName() && (el.getType() != Element::TYPED_OBJECT_AMF0)) {
  794     if (buf) {
  795         bigbuf.reset(new cygnal::Buffer(el.getNameSize() + sizeof(boost::uint16_t) + buf->size()));
  796     } else {
  797         bigbuf.reset(new cygnal::Buffer(el.getNameSize() + sizeof(boost::uint16_t)));
  798     }
  799     
  800     // Add the length of the string for the name of the variable
  801     size_t length = el.getNameSize();
  802     boost::uint16_t enclength = length;
  803     swapBytes(&enclength, 2);
  804     *bigbuf = enclength;
  805     // Now the name itself
  806     std::string name = el.getName();
  807     if (name.size() > 0) {
  808         *bigbuf += name;
  809     }
  810     if (buf) {
  811         *bigbuf += buf;
  812     }
  813     return bigbuf;
  814     }
  815     
  816     return buf;
  817 }
  818 
  819 /// Encode a variable to it's serialized representation.
  820 ///
  821 /// @param el A smart pointer to the Element to encode.
  822 ///
  823 /// @return a binary AMF packet in big endian format
  824 boost::shared_ptr<Buffer>
  825 AMF::encodeProperty(boost::shared_ptr<cygnal::Element> el)
  826 {
  827 //    GNASH_REPORT_FUNCTION;
  828     size_t outsize;
  829     
  830     outsize = el->getNameSize() + el->getDataSize() + AMF_PROP_HEADER_SIZE;
  831 
  832     boost::shared_ptr<Buffer> buf(new Buffer(outsize));
  833     _totalsize += outsize;
  834 
  835     // Add the length of the string for the name of the property
  836     size_t length = el->getNameSize();
  837     boost::uint16_t enclength = length;
  838     swapBytes(&enclength, 2);
  839     *buf = enclength;
  840 
  841     if (el->getName()) {
  842     std::string name = el->getName();
  843     if (name.size() > 0) {
  844         *buf += name;
  845     }
  846     }
  847 
  848     // Add the type of the property's data
  849     *buf += el->getType();
  850     // Booleans appear to be encoded weird. Just a short after
  851     // the type byte that's the value.
  852     switch (el->getType()) {
  853       case Element::BOOLEAN_AMF0:
  854 //        enclength = el->to_bool();
  855 //        buf->append(enclength);
  856       *buf += el->to_bool();
  857       break;
  858       case Element::NUMBER_AMF0:
  859       if (el->to_reference()) {
  860           swapBytes(el->to_reference(), AMF0_NUMBER_SIZE);
  861           buf->append(el->to_reference(), AMF0_NUMBER_SIZE);
  862       }
  863       break;
  864       default:
  865       enclength = el->getDataSize();
  866       swapBytes(&enclength, 2);
  867       *buf += enclength;
  868       // Now the data for the property
  869       buf->append(el->to_reference(), el->getDataSize());
  870     }
  871     
  872     return buf;
  873 }
  874 
  875 /// \brief Extract an AMF object from an array of raw bytes.
  876 ///
  877 /// @param buf A smart pointer to a Buffer to parse the data from.
  878 ///
  879 /// @return A smart ptr to an Element.
  880 ///
  881 /// @remarks May throw a ParserException
  882 boost::shared_ptr<cygnal::Element> 
  883 AMF::extractAMF(boost::shared_ptr<Buffer> buf)
  884 {
  885 //    GNASH_REPORT_FUNCTION;
  886     boost::uint8_t* start = buf->reference();
  887     boost::uint8_t* tooFar = start+buf->size();
  888     
  889     return extractAMF(start, tooFar);
  890 }
  891 
  892 /// \brief Extract an AMF object from an array of raw bytes.
  893 /// An AMF object is one of the support data types.
  894 ///
  895 /// @param in A real pointer to the raw data to start parsing from.
  896 ///
  897 /// @param tooFar A pointer to one-byte-past the last valid memory
  898 /// address within the buffer.
  899 ///
  900 /// @return A smart ptr to an Element.
  901 ///
  902 /// @remarks May throw a ParserException
  903 boost::shared_ptr<cygnal::Element> 
  904 AMF::extractAMF(boost::uint8_t *in, boost::uint8_t* tooFar)
  905 {
  906 //    GNASH_REPORT_FUNCTION;
  907 
  908     boost::uint8_t *tmpptr = in;
  909     boost::uint16_t length;
  910     boost::shared_ptr<cygnal::Element> el(new Element);
  911 
  912     if (in == 0) {
  913         gnash::log_error(_("AMF body input data is NULL"));
  914         return el;
  915     }
  916 
  917     std::map<boost::uint16_t, cygnal::Element> references;
  918     
  919     // All elements look like this:
  920     // the first two bytes is the length of name of the element
  921     // Then the next bytes are the element name
  922     // After the element name there is a type byte. If it's a Number type, then
  923     // 8 bytes are read If it's a String type, then there is a count of
  924     // characters, then the string value    
  925     
  926     // Get the type of the element, which is a single byte.
  927     // This type casting looks like a stupid mistake, but it's
  928     // mostly to make valgrind shut up, as it has a tendency to
  929     // complain about legit code when it comes to all this byte
  930     // manipulation stuff.
  931     AMF amf_obj;
  932 
  933     // Jump through hoops to get the type so valgrind stays happy
  934     //    char c = *(reinterpret_cast<char *>(tmpptr));
  935 
  936     if (tooFar - tmpptr < 1) {
  937         gnash::log_error(_("AMF data too short to contain type field"));
  938         return el;
  939     }
  940 
  941     Element::amf0_type_e type = static_cast<Element::amf0_type_e>(*tmpptr);
  942     // skip past the header type field byte 
  943     ++tmpptr;
  944 
  945     switch (type) {
  946         case Element::NOTYPE:
  947         {
  948         gnash::log_error(_("Element has no type!"));
  949         break;
  950     }
  951         case Element::NUMBER_AMF0:
  952         {
  953             // Make sure this isn't less than 0. We check this above at
  954             // the moment.
  955             assert(tooFar >= tmpptr);
  956             
  957             if (static_cast<size_t>(tooFar - tmpptr) < sizeof(const double)) {
  958                 gnash::log_error(_("AMF data segment too short to contain"
  959                             "type NUMBER"));
  960         el.reset();
  961                 return el;
  962             }
  963             double swapped = *(reinterpret_cast<const double*>(tmpptr));
  964             swapBytes(&swapped, cygnal::AMF0_NUMBER_SIZE);
  965             el->makeNumber(swapped); 
  966             tmpptr += AMF0_NUMBER_SIZE; // all numbers are 8 bit big endian
  967             break;
  968         }
  969         case Element::BOOLEAN_AMF0:
  970             el->makeBoolean(tmpptr);
  971             tmpptr += 1;        // sizeof(bool) isn't always 1 for all compilers 
  972             break;
  973         case Element::STRING_AMF0:
  974             // get the length of the name
  975             length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
  976             tmpptr += sizeof(boost::uint16_t);
  977             if (length >= SANE_STR_SIZE) {
  978                 gnash::log_error(_("%d bytes for a string is over the safe "
  979                                    "limit of %d, line %d"), length,
  980                         SANE_STR_SIZE, __LINE__);
  981                 el.reset();
  982                 return el;
  983             }
  984             //log_debug(_("AMF String length is: %d"), length);
  985             if (length > 0) {
  986                 // get the name of the element
  987                 el->makeString(tmpptr, length);
  988                 //log_debug(_("AMF String is: %s"), el->to_string());
  989                 tmpptr += length;
  990             } else {
  991                 el->setType(Element::STRING_AMF0);
  992             }
  993             break;
  994         case Element::OBJECT_AMF0:
  995         {
  996             el->makeObject();
  997             while (tmpptr < tooFar) { 
  998                 // FIXME: was tooFar - AMF_HEADER_SIZE) 
  999                 if (*tmpptr+3 == TERMINATOR) {
 1000                     //log_debug("No data associated with Property "
 1001                     //"in object");
 1002                     tmpptr++;
 1003                     break;
 1004                 }
 1005                 boost::shared_ptr<cygnal::Element> child =
 1006                     amf_obj.extractProperty(tmpptr, tooFar); 
 1007                 if (child == 0) {
 1008                     // skip past zero length string (2 bytes), null
 1009                     // (1 byte) and end object (1 byte)
 1010                     //tmpptr += 3;
 1011                     break;
 1012                 }
 1013                 //child->dump();
 1014                 el->addProperty(child);
 1015                 tmpptr += amf_obj.totalsize();
 1016             }
 1017             tmpptr += AMF_HEADER_SIZE;      // skip past the terminator bytes
 1018             break;
 1019         }
 1020         case Element::MOVIECLIP_AMF0:
 1021             gnash::log_debug(_("AMF0 MovieClip frame"));
 1022             break;
 1023         case Element::NULL_AMF0:
 1024             el->makeNull();
 1025             break;
 1026         case Element::UNDEFINED_AMF0:
 1027             el->makeUndefined();
 1028             break;
 1029         case Element::REFERENCE_AMF0:
 1030         {
 1031             length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
 1032             tmpptr += sizeof(boost::uint16_t);
 1033             el->makeReference(length);
 1034             // FIXME: connect reference Element to the object
 1035             // pointed to by the index.
 1036             tmpptr += 3;
 1037             break;
 1038         }
 1039         // An ECMA array is composed of any of the data types. Much
 1040         // like an Object, the ECMA array is terminated by the
 1041         // end of object bytes, so parse till then.
 1042         case Element::ECMA_ARRAY_AMF0:
 1043         {
 1044             el->makeECMAArray();
 1045             tmpptr += sizeof(boost::uint32_t);
 1046 #if 1
 1047             while (tmpptr < tooFar) { // FIXME: was tooFar - AMF_HEADER_SIZE)
 1048                 if (*tmpptr+3 == TERMINATOR) {
 1049           //          log_debug("No data associated with Property in object");
 1050                     tmpptr++;
 1051                     break;
 1052                 }
 1053                 boost::shared_ptr<cygnal::Element> child =
 1054                     amf_obj.extractProperty(tmpptr, tooFar); 
 1055                 if (child == 0) {
 1056                     // skip past zero length string (2 bytes),
 1057                     // null (1 byte) and end object (1 byte)
 1058                     //tmpptr += 3;
 1059                     break;
 1060                 }
 1061       //          child->dump();
 1062                 el->addProperty(child);
 1063                 tmpptr += amf_obj.totalsize();
 1064             }
 1065             tmpptr += AMF_HEADER_SIZE;      // skip past the terminator bytes
 1066             break;
 1067 #else
 1068             // get the number of elements in the array
 1069             boost::uint32_t items = 
 1070                 ntohl((*(boost::uint32_t *)tmpptr) & 0xffffffff);
 1071   
 1072             tmpptr += sizeof(boost::uint32_t);
 1073             while (items--) {
 1074                 boost::shared_ptr<cygnal::Element> child =
 1075                     amf_obj.extractProperty(tmpptr, tooFar); 
 1076                 if (child == 0) {
 1077                     break;
 1078                 }
 1079                 child->dump();
 1080                 el->addProperty(child);
 1081                 tmpptr += amf_obj.totalsize();
 1082             }
 1083             tmpptr += AMF_HEADER_SIZE;      // skip past the terminator bytes
 1084 #endif
 1085             break;
 1086         }
 1087         case Element::OBJECT_END_AMF0:
 1088             // A strict array is only numbers
 1089             break;
 1090         case Element::STRICT_ARRAY_AMF0:
 1091         {
 1092             el->makeStrictArray();
 1093             // get the number of numbers in the array
 1094             boost::uint32_t items = ntohl((*(boost::uint32_t *)tmpptr));
 1095             // Skip past the length field to get to the start of the data
 1096             tmpptr += sizeof(boost::uint32_t);
 1097             while (items) {
 1098                 boost::shared_ptr<cygnal::Element> child =
 1099                     amf_obj.extractAMF(tmpptr, tooFar); 
 1100                 if (child == 0) {
 1101                     break;
 1102                 } else {
 1103                 //child->dump();
 1104                     el->addProperty(child);
 1105                     tmpptr += amf_obj.totalsize();
 1106                     --items;
 1107                 }
 1108             }
 1109             break;
 1110         }
 1111         case Element::DATE_AMF0:
 1112         {
 1113             double swapped = *reinterpret_cast<const double*>(tmpptr);
 1114             swapBytes(&swapped, cygnal::AMF0_NUMBER_SIZE);
 1115             el->makeDate(swapped);
 1116             tmpptr += AMF0_NUMBER_SIZE; // all dates are 8 bit big endian numbers
 1117             break;
 1118         }
 1119         case Element::LONG_STRING_AMF0:
 1120             el->makeLongString(tmpptr);
 1121             break;
 1122         case Element::UNSUPPORTED_AMF0:
 1123             el->makeUnsupported(tmpptr);
 1124             tmpptr += 1;
 1125             break;
 1126         case Element::RECORD_SET_AMF0:
 1127             el->makeRecordSet(tmpptr);
 1128             break;
 1129         case Element::XML_OBJECT_AMF0:
 1130             el->makeXMLObject(tmpptr);
 1131             break;
 1132         case Element::TYPED_OBJECT_AMF0:
 1133         {
 1134             el->makeTypedObject();
 1135             
 1136             length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
 1137             tmpptr += sizeof(boost::uint16_t);
 1138             if (length > 0) {
 1139                 std::string name(reinterpret_cast<const char*>(tmpptr), length);
 1140                 //log_debug("Typed object name is: %s", el->getName());
 1141                 el->setName(name.c_str(), name.size());
 1142             }
 1143             // Don't read past the end
 1144             if (tmpptr + length < tooFar) {
 1145                 tmpptr += length;
 1146             }
 1147           
 1148             while (tmpptr < tooFar - length) { 
 1149                 // FIXME: was tooFar - AMF_HEADER_SIZE)
 1150                 if (*(tmpptr +3) == TERMINATOR) {
 1151                     gnash::log_debug(_("Found object terminator byte"));
 1152                     tmpptr++;
 1153                     break;
 1154                 }
 1155                 boost::shared_ptr<cygnal::Element> child =
 1156                     amf_obj.extractProperty(tmpptr, tooFar); 
 1157                 if (child == 0) {
 1158                     break;
 1159                 }
 1160                 el->addProperty(child);
 1161                 tmpptr += amf_obj.totalsize();
 1162             }
 1163             //    el->dump();
 1164             tmpptr += AMF_HEADER_SIZE;      // skip past the terminator bytes
 1165             break;
 1166         }
 1167         case Element::AMF3_DATA:
 1168         default:
 1169             gnash::log_unimpl(_("%s: type %d"), __PRETTY_FUNCTION__, (int)type);
 1170             el.reset();
 1171             return el;
 1172     }
 1173         
 1174     // Calculate the offset for the next read
 1175     _totalsize = (tmpptr - in);
 1176 
 1177     return el;
 1178 }
 1179 
 1180 /// \brief Extract a Property.
 1181 /// A Property is a standard AMF object preceeded by a
 1182 /// length and an ASCII name field. These are only used
 1183 /// with higher level ActionScript objects.
 1184 ///
 1185 /// @param buf A smart pointer to an Buffer to parse the data from.
 1186 ///
 1187 /// @return A smart ptr to an Element.
 1188 ///
 1189 /// @remarks May throw a ParserException
 1190 boost::shared_ptr<cygnal::Element> 
 1191 AMF::extractProperty(boost::shared_ptr<Buffer> buf)
 1192 {
 1193 //    GNASH_REPORT_FUNCTION;
 1194 
 1195     boost::uint8_t* start = buf->reference();
 1196     boost::uint8_t* tooFar = start+buf->size();
 1197     return extractProperty(start, tooFar);
 1198 }
 1199 
 1200 /// \brief Extract a Property.
 1201 /// A Property is a standard AMF object preceeded by a
 1202 /// length and an ASCII name field. These are onicly used
 1203 /// with higher level ActionScript objects.
 1204 ///
 1205 /// @param in A real pointer to the raw data to start parsing from.
 1206 ///
 1207 /// @param tooFar A pointer to one-byte-past the last valid memory
 1208 /// address within the buffer.
 1209 ///
 1210 /// @return A smart ptr to an Element.
 1211 ///
 1212 /// @remarks May throw a ParserException
 1213 boost::shared_ptr<cygnal::Element> 
 1214 AMF::extractProperty(boost::uint8_t *in, boost::uint8_t* tooFar)
 1215 {
 1216 //    GNASH_REPORT_FUNCTION;
 1217     
 1218     boost::uint8_t *tmpptr = in;
 1219     boost::uint16_t length;
 1220     boost::shared_ptr<cygnal::Element> el;
 1221 
 1222     length = ntohs((*(boost::uint16_t *)tmpptr) & 0xffff);
 1223     // go past the length bytes, which leaves us pointing at the raw data
 1224     tmpptr += sizeof(boost::uint16_t);
 1225 
 1226     // sanity check the length of the data. The length is usually only zero if
 1227     // we've gone all the way to the end of the object.
 1228 
 1229     // valgrind complains if length is unintialized, which as we just set
 1230     // length to a value, this is tottaly bogus, and I'm tired of
 1231     // braindamaging code to keep valgrind happy.
 1232     if (length <= 0) {
 1233 //  log_debug("No Property name, object done %x, %x", (void *)in, (void *)tooFar);
 1234     return el;
 1235     }
 1236 
 1237 #if 0    
 1238     if (length + tmpptr > tooFar) {
 1239     log_error("%d bytes for a string is over the safe limit of %d. Putting the rest of the buffer into the string, line %d", length, SANE_STR_SIZE, __LINE__);
 1240     length = tooFar - tmpptr;
 1241     }    
 1242 #else
 1243     if (length >= SANE_STR_SIZE) {
 1244     gnash::log_error("%d bytes for a string is over the safe limit of %d. Putting the rest of the buffer into the string, line %d", length, SANE_STR_SIZE, __LINE__);
 1245     }    
 1246 #endif
 1247     
 1248     // name is just debugging help to print cleaner, and should be removed later
 1249 //    log_debug(_("AMF property name length is: %d"), length);
 1250     std::string name(reinterpret_cast<const char *>(tmpptr), length);
 1251 //    log_debug(_("AMF property name is: %s"), name);
 1252     // Don't read past the end
 1253     if (tmpptr + length < tooFar) {
 1254     tmpptr += length;
 1255     }
 1256     
 1257     char c = *(reinterpret_cast<char *>(tmpptr));
 1258     Element::amf0_type_e type = static_cast<Element::amf0_type_e>(c);
 1259     // If we get a NULL object, there is no data. In that case, we only return
 1260     // the name of the property.
 1261     if (type == Element::NULL_AMF0) {
 1262     gnash::log_debug(_("No data associated with Property \"%s\""), name);
 1263     el.reset(new Element);
 1264     el->setName(name.c_str(), name.size());
 1265     tmpptr += 1;
 1266     // Calculate the offset for the next read
 1267     } else {
 1268     // process the data with associated with the property.
 1269     // Go past the data to the start of the next AMF object, which
 1270     // should be a type byte.
 1271 //  tmpptr += length;
 1272     el = extractAMF(tmpptr, tooFar);
 1273     if (el) {
 1274         el->setName(name.c_str(), name.size()); // FIXME: arg, overwrites the name for TypedObjects
 1275     }
 1276     tmpptr += totalsize();
 1277     }
 1278 
 1279     //delete name;
 1280     
 1281     // Calculate the offset for the next read
 1282     _totalsize = (tmpptr - in);
 1283 
 1284     return el;    
 1285 }
 1286 
 1287 } // end of amf namespace
 1288 
 1289 // local Variables:
 1290 // mode: C++
 1291 // indent-tabs-mode: nil
 1292 // End: