Urbi SDK Remote for C++  2.7.5
ubinary.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009-2011, Gostai S.A.S.
00003  *
00004  * This software is provided "as is" without warranty of any kind,
00005  * either expressed or implied, including but not limited to the
00006  * implied warranties of fitness for a particular purpose.
00007  *
00008  * See the LICENSE file for more information.
00009  */
00010 
00012 
00013 #include <iostream>
00014 #include <sstream>
00015 #include <libport/debug.hh>
00016 #include <libport/escape.hh>
00017 #include <libport/foreach.hh>
00018 #include <libport/format.hh>
00019 #include <boost/algorithm/string/trim.hpp>
00020 #include <urbi/ubinary.hh>
00021 #include <urbi/uvalue.hh> // kernelMajor.
00022 
00023 GD_CATEGORY(Urbi.UValue);
00024 
00025 namespace urbi
00026 {
00027 
00028   UBinary::UBinary()
00029     : type(BINARY_NONE)
00030     , allocated_(true)
00031     , temporary_(false)
00032   {
00033     common.data = 0;
00034     common.size = 0;
00035   }
00036 
00037   UBinary::UBinary(const UBinary& b, bool copy, bool temp)
00038     : type(BINARY_NONE)
00039     , allocated_(copy)
00040     , temporary_(temp)
00041   {
00042     common.data = 0;
00043     if (copy)
00044       *this = b;
00045     else
00046     {
00047       // Be safe, do not try to guess which is bigger.
00048       image = b.image;
00049       sound = b.sound;
00050       message = b.message;
00051       type = b.type;
00052     }
00053   }
00054 
00055   UBinary::UBinary(const UImage& i, bool copy)
00056     : type(BINARY_IMAGE)
00057     , image(i)
00058     , allocated_(copy)
00059     , temporary_(false)
00060   {
00061     if (copy)
00062     {
00063       image.data = static_cast<unsigned char*> (malloc (image.size));
00064       memcpy(image.data, i.data, image.size);
00065     }
00066   }
00067 
00068   UBinary::UBinary(const USound& i, bool copy)
00069     : type(BINARY_SOUND)
00070     , sound(i)
00071     , allocated_(copy)
00072     , temporary_(false)
00073   {
00074     if (copy)
00075     {
00076       sound.data = static_cast<char*> (malloc (sound.size));
00077       memcpy(sound.data, i.data, sound.size);
00078     }
00079   }
00080 
00081   void
00082   UBinary::clear()
00083   {
00084     if (allocated_)
00085     {
00086       free(common.data);
00087       common.data = 0;
00088       common.size = 0;
00089     }
00090   }
00091 
00092   UBinary::~UBinary()
00093   {
00094     clear();
00095   }
00096 
00097   UBinary& UBinary::operator= (const UBinary& b)
00098   {
00099     if (this == &b)
00100       return *this;
00101 
00102     clear();
00103     if (b.temporary_)
00104     {
00105       // Be safe, do not try to guess which is bigger.
00106       image = b.image;
00107       sound = b.sound;
00108       message = b.message;
00109       type = b.type;
00110       UBinary& bb = const_cast<UBinary&>(b);
00111       bb.common.data = 0;
00112       bb.type = BINARY_NONE;
00113       temporary_ = true;
00114       return *this;
00115     }
00116     type = b.type;
00117     message = b.message;
00118     common.size = b.common.size;
00119     switch(type)
00120     {
00121       case BINARY_IMAGE:
00122         image = b.image;
00123         break;
00124       case BINARY_SOUND:
00125         sound = b.sound;
00126         break;
00127       case BINARY_NONE:
00128       case BINARY_UNKNOWN:
00129         break;
00130     }
00131     common.data = malloc(common.size);
00132     memcpy(common.data, b.common.data, b.common.size);
00133     return *this;
00134   }
00135 
00136   int
00137   UBinary::parse(const char* message, int pos,
00138                  const binaries_type& bins,
00139                  binaries_type::const_iterator& binpos, bool copy)
00140   {
00141     std::istringstream is(message + pos);
00142     bool ok = parse(is, bins, binpos, copy);
00143     // tellg() might be -1 if we encountered an error.
00144     int endpos = is.tellg();
00145     if (endpos == -1)
00146       endpos = strlen(message) - pos;
00147     return (ok ? 1:-1) * (pos + endpos);
00148   }
00149 
00150   namespace
00151   {
00155     static
00156     std::string
00157     headers_get(std::istringstream& i)
00158     {
00159       std::string res;
00160       int c = 0;
00161       while (!i.eof()
00162              && (c = i.get()) && c != '\n' && c != ';')
00163         res.append(1, c);
00164       if (i.eof())
00165         GD_ERROR("unexpected end of file while parsing UBinary headers");
00166       else
00167       {
00168         // Skip the delimiter.
00169         if (c == '\n')
00170         {
00171           if (i.peek() == '\r')
00172             i.ignore();
00173         }
00174       }
00175       // Remove leading/trailing spaces.
00176       boost::algorithm::trim(res);
00177       return res;
00178     }
00179   }
00180 
00181 
00182   bool
00183   UBinary::parse(std::istringstream& is,
00184                  const binaries_type& bins,
00185                  binaries_type::const_iterator& binpos, bool copy)
00186 
00187   {
00188     // LIBPORT_ECHO("Parsing: {" << is.str() << "}");
00189     if (binpos == bins.end())
00190     {
00191       GD_ERROR("no binary data available");
00192       return false;
00193     }
00194 
00195     // Validate size.
00196     size_t psize;
00197     is >> psize;
00198     if (is.fail())
00199     {
00200       GD_FERROR("cannot read bin size: %s (%s)", is.str(), psize);
00201       return false;
00202     }
00203     if (psize != binpos->size)
00204     {
00205       GD_FERROR("bin size inconsistency: %s != %s", psize, binpos->size);
00206       return false;
00207     }
00208     common.size = psize;
00209     if (copy)
00210     {
00211       common.data = malloc(common.size);
00212       memcpy(common.data, binpos->data, common.size);
00213     }
00214     else
00215     {
00216       common.data = binpos->data;
00217       this->allocated_ = false;
00218     }
00219     ++binpos;
00220 
00221     // Skip spaces.
00222     while (is.peek() == ' ')
00223       is.ignore();
00224 
00225     // Get the headers.
00226     message = headers_get(is);
00227 
00228     // Analyse the header to decode know UBinary types.
00229     // Header stream.
00230     std::istringstream hs(message);
00231 
00232     // Parse the optional type.  Don't check hs.fail, since the type
00233     // is optional, in which case t remains empty.
00234     std::string t;
00235     hs >> t;
00236     UImageFormat image_format = parse_image_format(t);
00237     if (image_format != IMAGE_UNKNOWN || t.find("image_")==0)
00238     {
00239       type = BINARY_IMAGE;
00240       image.size = common.size;
00241       // In some cases (jpeg source), image size is not present in headers.
00242       image.width = image.height = 0;
00243       hs >> image.width >> image.height;
00244       image.imageFormat = image_format;
00245     }
00246     else if (t == "raw" || t == "wav")
00247     {
00248       type = BINARY_SOUND;
00249       sound.soundFormat = parse_sound_format(t);
00250       sound.size = common.size;
00251       hs >> sound.channels
00252          >> sound.rate
00253          >> sound.sampleSize >> sound.sampleFormat;
00254     }
00255     else
00256     {
00257       // GD_FWARN("unknown binary type: %s", t);
00258       type = BINARY_UNKNOWN;
00259     }
00260 
00261     return true;
00262   }
00263 
00264   void UBinary::buildMessage()
00265   {
00266     message = getMessage();
00267   }
00268 
00269   std::string UBinary::getMessage() const
00270   {
00271     switch (type)
00272     {
00273     case BINARY_IMAGE:
00274       return image.headers_();
00275     case BINARY_SOUND:
00276       return sound.headers_();
00277     case BINARY_UNKNOWN:
00278       {
00279         bool warned = false;
00280         std::string res = message;
00281         foreach (char& c, res)
00282           if (c == '\0' || c == '\n' || c == ';')
00283           {
00284             if (!warned)
00285             {
00286               GD_FERROR("invalid UBinary header: "
00287                         "prohibited `\\n', `\\0' and `;' will be "
00288                         "smashed to space: %s",
00289                         libport::escape(message));
00290               warned = true;
00291             }
00292             c = ' ';
00293           }
00294         // Remove leading/trailing spaces.
00295         boost::algorithm::trim(res);
00296         return res;
00297       }
00298     case BINARY_NONE:
00299       return "";
00300     }
00301     unreachable();
00302   }
00303 
00304   std::ostream&
00305   UBinary::print(std::ostream& o, int kernelMajor) const
00306   {
00307     if (2 <= kernelMajor)
00308     {
00309       o << libport::format("Global.Binary.new(\"%s\", \"\\B(%s)(",
00310                            getMessage(), common.size);
00311       o.write((char*) common.data, common.size);
00312       o << ")\")";
00313     }
00314     else
00315     {
00316       // Format for the Kernel, which wants ';' as header terminator.
00317       o << "BIN " << common.size;
00318       const std::string h = getMessage();
00319       if (!h.empty())
00320         o << ' ' << h;
00321       o << ';';
00322       o.write((char*) common.data, common.size);
00323     }
00324     return o;
00325   }
00326 
00327   std::ostream&
00328   operator<< (std::ostream& o, const UBinary& t)
00329   {
00330     return t.print(o, ::urbi::kernelMajor(o));
00331   }
00332 
00333 } // namespace urbi