<commit_before>// ArduinoJson - https://arduinojson.org
// Copyright Benoit Blanchon 2014-2021
// MIT License

#pragma once

#include <stdint.h>
#include <string.h>  // for strlen

#include <ArduinoJson/Json/EscapeSequence.hpp>
#include <ArduinoJson/Numbers/FloatParts.hpp>
#include <ArduinoJson/Numbers/Integer.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/attributes.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Serialization/CountingDecorator.hpp>

namespace ARDUINOJSON_NAMESPACE {

template <typename TWriter>
class TextFormatter {
 public:
  explicit TextFormatter(TWriter writer) : _writer(writer) {}

  // Returns the number of bytes sent to the TWriter implementation.
  size_t bytesWritten() const {
    return _writer.count();
  }

  void writeBoolean(bool value) {
    if (value)
      writeRaw("true");
    else
      writeRaw("false");
  }

  void writeString(const char *value) {
    ARDUINOJSON_ASSERT(value != NULL);
    writeRaw('\"');
    while (*value) writeChar(*value++);
    writeRaw('\"');
  }

  void writeChar(char c) {
    char specialChar = EscapeSequence::escapeChar(c);
    if (specialChar) {
      writeRaw('\\');
      writeRaw(specialChar);
    } else {
      writeRaw(c);
    }
  }

  template <typename T>
  void writeFloat(T value) {
    if (isnan(value))
      return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");

#if ARDUINOJSON_ENABLE_INFINITY
    if (value < 0.0) {
      writeRaw('-');
      value = -value;
    }

    if (isinf(value))
      return writeRaw("Infinity");
#else
    if (isinf(value))
      return writeRaw("null");

    if (value < 0.0) {
      writeRaw('-');
      value = -value;
    }
#endif

    FloatParts<T> parts(value);

    writeInteger(parts.integral);
    if (parts.decimalPlaces)
      writeDecimals(parts.decimal, parts.decimalPlaces);

    if (parts.exponent) {
      writeRaw('e');
      writeInteger(parts.exponent);
    }
  }

  template <typename T>
  typename enable_if<is_signed<T>::value>::type writeInteger(T value) {
    typedef typename make_unsigned<T>::type unsigned_type;
    unsigned_type unsigned_value;
    if (value < 0) {
      writeRaw('-');
      unsigned_value = static_cast<unsigned_type>(-value);
    } else {
      unsigned_value = static_cast<unsigned_type>(value);
    }
    writeInteger(unsigned_value);
  }

  template <typename T>
  typename enable_if<is_unsigned<T>::value>::type writeInteger(T value) {
    char buffer[22];
    char *end = buffer + sizeof(buffer);
    char *begin = end;

    // write the string in reverse order
    do {
      *--begin = char(value % 10 + '0');
      value = T(value / 10);
    } while (value);

    // and dump it in the right order
    writeRaw(begin, end);
  }

  void writeDecimals(uint32_t value, int8_t width) {
    // buffer should be big enough for all digits and the dot
    char buffer[16];
    char *end = buffer + sizeof(buffer);
    char *begin = end;

    // write the string in reverse order
    while (width--) {
      *--begin = char(value % 10 + '0');
      value /= 10;
    }
    *--begin = '.';

    // and dump it in the right order
    writeRaw(begin, end);
  }

  void writeRaw(const char *s) {
    _writer.write(reinterpret_cast<const uint8_t *>(s), strlen(s));
  }

  void writeRaw(const char *s, size_t n) {
    _writer.write(reinterpret_cast<const uint8_t *>(s), n);
  }

  void writeRaw(const char *begin, const char *end) {
    _writer.write(reinterpret_cast<const uint8_t *>(begin),
                  static_cast<size_t>(end - begin));
  }

  template <size_t N>
  void writeRaw(const char (&s)[N]) {
    _writer.write(reinterpret_cast<const uint8_t *>(s), N - 1);
  }
  void writeRaw(char c) {
    _writer.write(static_cast<uint8_t>(c));
  }

 protected:
  CountingDecorator<TWriter> _writer;
  size_t _length;

 private:
  TextFormatter &operator=(const TextFormatter &);  // cannot be assigned
};
}  // namespace ARDUINOJSON_NAMESPACE
<commit_msg>Fixed undefined behavior in JSON serializer<commit_after>// ArduinoJson - https://arduinojson.org
// Copyright Benoit Blanchon 2014-2021
// MIT License

#pragma once

#include <stdint.h>
#include <string.h>  // for strlen

#include <ArduinoJson/Json/EscapeSequence.hpp>
#include <ArduinoJson/Numbers/FloatParts.hpp>
#include <ArduinoJson/Numbers/Integer.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/attributes.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Serialization/CountingDecorator.hpp>

namespace ARDUINOJSON_NAMESPACE {

template <typename TWriter>
class TextFormatter {
 public:
  explicit TextFormatter(TWriter writer) : _writer(writer) {}

  // Returns the number of bytes sent to the TWriter implementation.
  size_t bytesWritten() const {
    return _writer.count();
  }

  void writeBoolean(bool value) {
    if (value)
      writeRaw("true");
    else
      writeRaw("false");
  }

  void writeString(const char *value) {
    ARDUINOJSON_ASSERT(value != NULL);
    writeRaw('\"');
    while (*value) writeChar(*value++);
    writeRaw('\"');
  }

  void writeChar(char c) {
    char specialChar = EscapeSequence::escapeChar(c);
    if (specialChar) {
      writeRaw('\\');
      writeRaw(specialChar);
    } else {
      writeRaw(c);
    }
  }

  template <typename T>
  void writeFloat(T value) {
    if (isnan(value))
      return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");

#if ARDUINOJSON_ENABLE_INFINITY
    if (value < 0.0) {
      writeRaw('-');
      value = -value;
    }

    if (isinf(value))
      return writeRaw("Infinity");
#else
    if (isinf(value))
      return writeRaw("null");

    if (value < 0.0) {
      writeRaw('-');
      value = -value;
    }
#endif

    FloatParts<T> parts(value);

    writeInteger(parts.integral);
    if (parts.decimalPlaces)
      writeDecimals(parts.decimal, parts.decimalPlaces);

    if (parts.exponent) {
      writeRaw('e');
      writeInteger(parts.exponent);
    }
  }

  template <typename T>
  typename enable_if<is_signed<T>::value>::type writeInteger(T value) {
    typedef typename make_unsigned<T>::type unsigned_type;
    unsigned_type unsigned_value;
    if (value < 0) {
      writeRaw('-');
      unsigned_value = unsigned_type(unsigned_type(~value) + 1);
    } else {
      unsigned_value = unsigned_type(value);
    }
    writeInteger(unsigned_value);
  }

  template <typename T>
  typename enable_if<is_unsigned<T>::value>::type writeInteger(T value) {
    char buffer[22];
    char *end = buffer + sizeof(buffer);
    char *begin = end;

    // write the string in reverse order
    do {
      *--begin = char(value % 10 + '0');
      value = T(value / 10);
    } while (value);

    // and dump it in the right order
    writeRaw(begin, end);
  }

  void writeDecimals(uint32_t value, int8_t width) {
    // buffer should be big enough for all digits and the dot
    char buffer[16];
    char *end = buffer + sizeof(buffer);
    char *begin = end;

    // write the string in reverse order
    while (width--) {
      *--begin = char(value % 10 + '0');
      value /= 10;
    }
    *--begin = '.';

    // and dump it in the right order
    writeRaw(begin, end);
  }

  void writeRaw(const char *s) {
    _writer.write(reinterpret_cast<const uint8_t *>(s), strlen(s));
  }

  void writeRaw(const char *s, size_t n) {
    _writer.write(reinterpret_cast<const uint8_t *>(s), n);
  }

  void writeRaw(const char *begin, const char *end) {
    _writer.write(reinterpret_cast<const uint8_t *>(begin),
                  static_cast<size_t>(end - begin));
  }

  template <size_t N>
  void writeRaw(const char (&s)[N]) {
    _writer.write(reinterpret_cast<const uint8_t *>(s), N - 1);
  }
  void writeRaw(char c) {
    _writer.write(static_cast<uint8_t>(c));
  }

 protected:
  CountingDecorator<TWriter> _writer;
  size_t _length;

 private:
  TextFormatter &operator=(const TextFormatter &);  // cannot be assigned
};
}  // namespace ARDUINOJSON_NAMESPACE
<|endoftext|>


<commit_before>// @(#)root/net:$Id$
// Author: Fons Rademakers   19/12/96

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TMessage                                                             //
//                                                                      //
// Message buffer class used for serializing objects and sending them   //
// over a network. This class inherits from TBuffer the basic I/O       //
// serializer.                                                          //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TMessage.h"
#include "TVirtualStreamerInfo.h"
#include "Bytes.h"
#include "TFile.h"
#include "TProcessID.h"

extern "C" void R__zip (Int_t cxlevel, Int_t *nin, char *bufin, Int_t *lout, char *bufout, Int_t *nout);
extern "C" void R__unzip(Int_t *nin, UChar_t *bufin, Int_t *lout, char *bufout, Int_t *nout);
const Int_t kMAXBUF = 0xffffff;

Bool_t TMessage::fgEvolution = kFALSE;

ClassImp(TMessage)

//______________________________________________________________________________
TMessage::TMessage(UInt_t what) : TBufferFile(TBuffer::kWrite)
{
   // Create a TMessage object for storing objects. The "what" integer
   // describes the type of message. Predifined ROOT system message types
   // can be found in MessageTypes.h. Make sure your own message types are
   // unique from the ROOT defined message types (i.e. 0 - 10000 are
   // reserved by ROOT). In case you OR "what" with kMESS_ACK, the message
   // will wait for an acknowledgement from the remote side. This makes
   // the sending process synchronous. In case you OR "what" with kMESS_ZIP,
   // the message will be compressed in TSocket using the zip algorithm
   // (only if message is > 256 bytes).

   // space at the beginning of the message reserved for the message length
   UInt_t   reserved = 0;
   *this << reserved;

   fWhat  = what;
   *this << what;

   fClass      = 0;
   fCompress   = 0;
   fBufComp    = 0;
   fBufCompCur = 0;
   fCompPos    = 0;
   fInfos      = 0;
   fEvolution  = kFALSE;

   SetBit(kCannotHandleMemberWiseStreaming);
}

//______________________________________________________________________________
TMessage::TMessage(void *buf, Int_t bufsize) : TBufferFile(TBuffer::kRead, bufsize, buf)
{
   // Create a TMessage object for reading objects. The objects will be
   // read from buf. Use the What() method to get the message type.

   // skip space at the beginning of the message reserved for the message length
   fBufCur += sizeof(UInt_t);

   *this >> fWhat;

   fCompress   = 0;
   fBufComp    = 0;
   fBufCompCur = 0;
   fCompPos    = 0;
   fInfos      = 0;
   fEvolution  = kFALSE;

   if (fWhat & kMESS_ZIP) {
      // if buffer has kMESS_ZIP set, move it to fBufComp and uncompress
      fBufComp    = fBuffer;
      fBufCompCur = fBuffer + bufsize;
      fBuffer     = 0;
      Uncompress();
   }

   if (fWhat == kMESS_OBJECT) {
      InitMap();
      fClass = ReadClass();     // get first the class stored in message
      SetBufferOffset(sizeof(UInt_t) + sizeof(fWhat));
      ResetMap();
   } else {
      fClass = 0;
   }
}

//______________________________________________________________________________
TMessage::~TMessage()
{
   // Clean up compression buffer.

   delete [] fBufComp;
   delete fInfos;
}

//______________________________________________________________________________
void TMessage::EnableSchemaEvolutionForAll(Bool_t enable)
{
   // Static function enabling or disabling the automatic schema evolution.
   // By default schema evolution support is off.

   fgEvolution = enable;
}

//______________________________________________________________________________
Bool_t TMessage::UsesSchemaEvolutionForAll()
{
   // Static function returning status of global schema evolution.

   return fgEvolution;
}

//______________________________________________________________________________
void TMessage::ForceWriteInfo(TVirtualStreamerInfo *info, Bool_t /* force */)
{
   // Force writing the TStreamerInfo to the message.

   if (fgEvolution || fEvolution) fInfos->Add(info);
}

//______________________________________________________________________________
void TMessage::Forward()
{
   // Change a buffer that was received into one that can be send, i.e.
   // forward a just received message.

   if (IsReading()) {
      SetWriteMode();
      SetBufferOffset(fBufSize);
      SetBit(kCannotHandleMemberWiseStreaming);

      if (fBufComp) {
         fCompPos = fBufCur;
      }
   }
}

//______________________________________________________________________________
void TMessage::IncrementLevel(TVirtualStreamerInfo *info)
{
   // Increment level.

   TBufferFile::IncrementLevel(info);

   if (fgEvolution || fEvolution) fInfos->Add(info);
}

//______________________________________________________________________________
void TMessage::Reset()
{
   // Reset the message buffer so we can use (i.e. fill) it again.

   SetBufferOffset(sizeof(UInt_t) + sizeof(fWhat));
   ResetMap();

   if (fBufComp) {
      delete [] fBufComp;
      fBufComp    = 0;
      fBufCompCur = 0;
      fCompPos    = 0;
   }
}

//______________________________________________________________________________
void TMessage::SetLength() const
{
   // Set the message length at the beginning of the message buffer.
   // This method is only called by TSocket::Send().

   if (IsWriting()) {
      char *buf = Buffer();
      tobuf(buf, (UInt_t)(Length() - sizeof(UInt_t)));

      if (fBufComp) {
         buf = fBufComp;
         tobuf(buf, (UInt_t)(CompLength() - sizeof(UInt_t)));
      }
   }
}

//______________________________________________________________________________
void TMessage::SetWhat(UInt_t what)
{
   // Using this method one can change the message type a-posteriory.
   // In case you OR "what" with kMESS_ACK, the message will wait for
   // an acknowledgement from the remote side. This makes the sending
   // process synchronous.

   fWhat = what;

   char *buf = Buffer();
   buf += sizeof(UInt_t);   // skip reserved length space
   tobuf(buf, what);

   if (fBufComp) {
      buf = fBufComp;
      buf += sizeof(UInt_t);   // skip reserved length space
      tobuf(buf, what | kMESS_ZIP);
   }
}

//______________________________________________________________________________
void TMessage::SetCompressionLevel(Int_t level)
{
   // Set the message compression level. Can be between 0 and 9 with 0
   // being no compression and 9 maximum compression. In general the default
   // level of 1 is the best compromise between achieved compression and
   // cpu time. Compression will only happen when the message is > 256 bytes.

   if (level < 0) level = 0;
   if (level > 9) level = 9;

   if (level != fCompress && fBufComp) {
      delete [] fBufComp;
      fBufComp    = 0;
      fBufCompCur = 0;
      fCompPos    = 0;
   }
   fCompress = level;
}

//______________________________________________________________________________
Int_t TMessage::Compress()
{
   // Compress the message. The message will only be compressed if the
   // compression level > 0 and the if the message is > 256 bytes.
   // Returns -1 in case of error (when compression fails or
   // when the message increases in size in some pathological cases),
   // otherwise returns 0.

   if (fCompress == 0) {
      // no compression specified
      if (fBufComp) {
         delete [] fBufComp;
         fBufComp    = 0;
         fBufCompCur = 0;
         fCompPos    = 0;
      }
      return 0;
   }

   if (fBufComp && fCompPos == fBufCur) {
      // the message was already compressed
      return 0;
   }

   // remove any existing compressed buffer before compressing modified message
   if (fBufComp) {
      delete [] fBufComp;
      fBufComp    = 0;
      fBufCompCur = 0;
      fCompPos    = 0;
   }

   if (Length() <= (Int_t)(256 + 2*sizeof(UInt_t))) {
      // this message is too small to be compressed
      return 0;
   }

   Int_t hdrlen   = 2*sizeof(UInt_t);
   Int_t messlen  = Length() - hdrlen;
   Int_t nbuffers = messlen / kMAXBUF;
   Int_t chdrlen  = 3*sizeof(UInt_t);   // compressed buffer header length
   Int_t buflen   = TMath::Max(512, chdrlen + messlen + 9*nbuffers);
   fBufComp       = new char[buflen];
   char *messbuf  = Buffer() + hdrlen;
   char *bufcur   = fBufComp + chdrlen;
   Int_t noutot   = 0;
   Int_t nzip     = 0;
   Int_t nout, bufmax;
   for (Int_t i = 0; i <= nbuffers; i++) {
      if (i == nbuffers)
         bufmax = messlen - nzip;
      else
         bufmax = kMAXBUF;
      R__zip(fCompress, &bufmax, messbuf, &bufmax, bufcur, &nout);
      if (nout == 0 || nout >= messlen) {
         //this happens when the buffer cannot be compressed
         delete [] fBufComp;
         fBufComp    = 0;
         fBufCompCur = 0;
         fCompPos    = 0;
         return -1;
      }
      bufcur  += nout;
      noutot  += nout;
      messbuf += kMAXBUF;
      nzip    += kMAXBUF;
   }
   fBufCompCur = bufcur;
   fCompPos    = fBufCur;

   bufcur = fBufComp;
   tobuf(bufcur, (UInt_t)(CompLength() - sizeof(UInt_t)));
   Int_t what = fWhat | kMESS_ZIP;
   tobuf(bufcur, what);
   tobuf(bufcur, Length());    // original uncompressed buffer length

   return 0;
}

//______________________________________________________________________________
Int_t TMessage::Uncompress()
{
   // Uncompress the message. The message will only be uncompressed when
   // kMESS_ZIP is set. Returns -1 in case of error, 0 otherwise.

   if (!fBufComp || !(fWhat & kMESS_ZIP))
      return -1;

   Int_t buflen;
   Int_t hdrlen = 2*sizeof(UInt_t);
   char *bufcur1 = fBufComp + hdrlen;
   frombuf(bufcur1, &buflen);
   UChar_t *bufcur = (UChar_t*)bufcur1;
   fBuffer  = new char[buflen];
   fBufSize = buflen;
   fBufCur  = fBuffer + sizeof(UInt_t) + sizeof(fWhat);
   fBufMax  = fBuffer + fBufSize;
   char *messbuf = fBuffer + hdrlen;

   Int_t nin, nout, nbuf;
   Int_t noutot = 0;
   while (1) {
      nin  = 9 + ((Int_t)bufcur[3] | ((Int_t)bufcur[4] << 8) | ((Int_t)bufcur[5] << 16));
      nbuf = (Int_t)bufcur[6] | ((Int_t)bufcur[7] << 8) | ((Int_t)bufcur[8] << 16);
      R__unzip(&nin, bufcur, &nbuf, messbuf, &nout);
      if (!nout) break;
      noutot += nout;
      if (noutot >= buflen - hdrlen) break;
      bufcur  += nin;
      messbuf += nout;
   }

   fWhat &= ~kMESS_ZIP;
   fCompress = 1;

   return 0;
}

//______________________________________________________________________________
void TMessage::WriteObject(const TObject *obj)
{
   // Write object to message buffer.
   // When support for schema evolution is enabled the list of TStreamerInfo
   // used to stream this object is kept in fInfos. This information is used
   // by TSocket::Send that sends this list through the socket. This list is in
   // turn used by TSocket::Recv to store the TStreamerInfo objects in the
   // relevant TClass in case the TClass does not know yet about a particular
   // class version. This feature is implemented to support clients and servers
   // with either different ROOT versions or different user classes versions.

   if (fgEvolution || fEvolution) {
      if (fInfos)
         fInfos->Clear();
      else
         fInfos = new TList();
   }

   fBitsPIDs.ResetAllBits();
   WriteObjectAny(obj, TObject::Class());
}

//______________________________________________________________________________
UShort_t TMessage::WriteProcessID(TProcessID *pid)
{
   // Check if the ProcessID pid is already in the message.
   // If not, then:
   //   - mark bit 0 of fBitsPIDs to indicate that a ProcessID has been found
   //   - mark bit uid+1 where uid id the uid of the ProcessID

   if (fBitsPIDs.TestBitNumber(0)) return 0;
   if (!pid)
      pid = TProcessID::GetPID();
   if (!pid) return 0;
   fBitsPIDs.SetBitNumber(0);
   UInt_t uid = pid->GetUniqueID();
   fBitsPIDs.SetBitNumber(uid+1);
   return 1;
}
<commit_msg>From Gerri: add some protections on fInfo being 0.<commit_after>// @(#)root/net:$Id$
// Author: Fons Rademakers   19/12/96

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TMessage                                                             //
//                                                                      //
// Message buffer class used for serializing objects and sending them   //
// over a network. This class inherits from TBuffer the basic I/O       //
// serializer.                                                          //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TMessage.h"
#include "TVirtualStreamerInfo.h"
#include "Bytes.h"
#include "TFile.h"
#include "TProcessID.h"

extern "C" void R__zip (Int_t cxlevel, Int_t *nin, char *bufin, Int_t *lout, char *bufout, Int_t *nout);
extern "C" void R__unzip(Int_t *nin, UChar_t *bufin, Int_t *lout, char *bufout, Int_t *nout);
const Int_t kMAXBUF = 0xffffff;

Bool_t TMessage::fgEvolution = kFALSE;

ClassImp(TMessage)

//______________________________________________________________________________
TMessage::TMessage(UInt_t what) : TBufferFile(TBuffer::kWrite)
{
   // Create a TMessage object for storing objects. The "what" integer
   // describes the type of message. Predifined ROOT system message types
   // can be found in MessageTypes.h. Make sure your own message types are
   // unique from the ROOT defined message types (i.e. 0 - 10000 are
   // reserved by ROOT). In case you OR "what" with kMESS_ACK, the message
   // will wait for an acknowledgement from the remote side. This makes
   // the sending process synchronous. In case you OR "what" with kMESS_ZIP,
   // the message will be compressed in TSocket using the zip algorithm
   // (only if message is > 256 bytes).

   // space at the beginning of the message reserved for the message length
   UInt_t   reserved = 0;
   *this << reserved;

   fWhat  = what;
   *this << what;

   fClass      = 0;
   fCompress   = 0;
   fBufComp    = 0;
   fBufCompCur = 0;
   fCompPos    = 0;
   fInfos      = 0;
   fEvolution  = kFALSE;

   SetBit(kCannotHandleMemberWiseStreaming);
}

//______________________________________________________________________________
TMessage::TMessage(void *buf, Int_t bufsize) : TBufferFile(TBuffer::kRead, bufsize, buf)
{
   // Create a TMessage object for reading objects. The objects will be
   // read from buf. Use the What() method to get the message type.

   // skip space at the beginning of the message reserved for the message length
   fBufCur += sizeof(UInt_t);

   *this >> fWhat;

   fCompress   = 0;
   fBufComp    = 0;
   fBufCompCur = 0;
   fCompPos    = 0;
   fInfos      = 0;
   fEvolution  = kFALSE;

   if (fWhat & kMESS_ZIP) {
      // if buffer has kMESS_ZIP set, move it to fBufComp and uncompress
      fBufComp    = fBuffer;
      fBufCompCur = fBuffer + bufsize;
      fBuffer     = 0;
      Uncompress();
   }

   if (fWhat == kMESS_OBJECT) {
      InitMap();
      fClass = ReadClass();     // get first the class stored in message
      SetBufferOffset(sizeof(UInt_t) + sizeof(fWhat));
      ResetMap();
   } else {
      fClass = 0;
   }
}

//______________________________________________________________________________
TMessage::~TMessage()
{
   // Clean up compression buffer.

   delete [] fBufComp;
   delete fInfos;
}

//______________________________________________________________________________
void TMessage::EnableSchemaEvolutionForAll(Bool_t enable)
{
   // Static function enabling or disabling the automatic schema evolution.
   // By default schema evolution support is off.

   fgEvolution = enable;
}

//______________________________________________________________________________
Bool_t TMessage::UsesSchemaEvolutionForAll()
{
   // Static function returning status of global schema evolution.

   return fgEvolution;
}

//______________________________________________________________________________
void TMessage::ForceWriteInfo(TVirtualStreamerInfo *info, Bool_t /* force */)
{
   // Force writing the TStreamerInfo to the message.

   if (fgEvolution || fEvolution) {
      if (!fInfos) fInfos = new TList();
      fInfos->Add(info);
   }
}

//______________________________________________________________________________
void TMessage::Forward()
{
   // Change a buffer that was received into one that can be send, i.e.
   // forward a just received message.

   if (IsReading()) {
      SetWriteMode();
      SetBufferOffset(fBufSize);
      SetBit(kCannotHandleMemberWiseStreaming);

      if (fBufComp) {
         fCompPos = fBufCur;
      }
   }
}

//______________________________________________________________________________
void TMessage::IncrementLevel(TVirtualStreamerInfo *info)
{
   // Increment level.

   TBufferFile::IncrementLevel(info);

   if (fgEvolution || fEvolution) {
      if (!fInfos) fInfos = new TList();
      fInfos->Add(info);
   }
}

//______________________________________________________________________________
void TMessage::Reset()
{
   // Reset the message buffer so we can use (i.e. fill) it again.

   SetBufferOffset(sizeof(UInt_t) + sizeof(fWhat));
   ResetMap();

   if (fBufComp) {
      delete [] fBufComp;
      fBufComp    = 0;
      fBufCompCur = 0;
      fCompPos    = 0;
   }
}

//______________________________________________________________________________
void TMessage::SetLength() const
{
   // Set the message length at the beginning of the message buffer.
   // This method is only called by TSocket::Send().

   if (IsWriting()) {
      char *buf = Buffer();
      tobuf(buf, (UInt_t)(Length() - sizeof(UInt_t)));

      if (fBufComp) {
         buf = fBufComp;
         tobuf(buf, (UInt_t)(CompLength() - sizeof(UInt_t)));
      }
   }
}

//______________________________________________________________________________
void TMessage::SetWhat(UInt_t what)
{
   // Using this method one can change the message type a-posteriory.
   // In case you OR "what" with kMESS_ACK, the message will wait for
   // an acknowledgement from the remote side. This makes the sending
   // process synchronous.

   fWhat = what;

   char *buf = Buffer();
   buf += sizeof(UInt_t);   // skip reserved length space
   tobuf(buf, what);

   if (fBufComp) {
      buf = fBufComp;
      buf += sizeof(UInt_t);   // skip reserved length space
      tobuf(buf, what | kMESS_ZIP);
   }
}

//______________________________________________________________________________
void TMessage::SetCompressionLevel(Int_t level)
{
   // Set the message compression level. Can be between 0 and 9 with 0
   // being no compression and 9 maximum compression. In general the default
   // level of 1 is the best compromise between achieved compression and
   // cpu time. Compression will only happen when the message is > 256 bytes.

   if (level < 0) level = 0;
   if (level > 9) level = 9;

   if (level != fCompress && fBufComp) {
      delete [] fBufComp;
      fBufComp    = 0;
      fBufCompCur = 0;
      fCompPos    = 0;
   }
   fCompress = level;
}

//______________________________________________________________________________
Int_t TMessage::Compress()
{
   // Compress the message. The message will only be compressed if the
   // compression level > 0 and the if the message is > 256 bytes.
   // Returns -1 in case of error (when compression fails or
   // when the message increases in size in some pathological cases),
   // otherwise returns 0.

   if (fCompress == 0) {
      // no compression specified
      if (fBufComp) {
         delete [] fBufComp;
         fBufComp    = 0;
         fBufCompCur = 0;
         fCompPos    = 0;
      }
      return 0;
   }

   if (fBufComp && fCompPos == fBufCur) {
      // the message was already compressed
      return 0;
   }

   // remove any existing compressed buffer before compressing modified message
   if (fBufComp) {
      delete [] fBufComp;
      fBufComp    = 0;
      fBufCompCur = 0;
      fCompPos    = 0;
   }

   if (Length() <= (Int_t)(256 + 2*sizeof(UInt_t))) {
      // this message is too small to be compressed
      return 0;
   }

   Int_t hdrlen   = 2*sizeof(UInt_t);
   Int_t messlen  = Length() - hdrlen;
   Int_t nbuffers = messlen / kMAXBUF;
   Int_t chdrlen  = 3*sizeof(UInt_t);   // compressed buffer header length
   Int_t buflen   = TMath::Max(512, chdrlen + messlen + 9*nbuffers);
   fBufComp       = new char[buflen];
   char *messbuf  = Buffer() + hdrlen;
   char *bufcur   = fBufComp + chdrlen;
   Int_t noutot   = 0;
   Int_t nzip     = 0;
   Int_t nout, bufmax;
   for (Int_t i = 0; i <= nbuffers; i++) {
      if (i == nbuffers)
         bufmax = messlen - nzip;
      else
         bufmax = kMAXBUF;
      R__zip(fCompress, &bufmax, messbuf, &bufmax, bufcur, &nout);
      if (nout == 0 || nout >= messlen) {
         //this happens when the buffer cannot be compressed
         delete [] fBufComp;
         fBufComp    = 0;
         fBufCompCur = 0;
         fCompPos    = 0;
         return -1;
      }
      bufcur  += nout;
      noutot  += nout;
      messbuf += kMAXBUF;
      nzip    += kMAXBUF;
   }
   fBufCompCur = bufcur;
   fCompPos    = fBufCur;

   bufcur = fBufComp;
   tobuf(bufcur, (UInt_t)(CompLength() - sizeof(UInt_t)));
   Int_t what = fWhat | kMESS_ZIP;
   tobuf(bufcur, what);
   tobuf(bufcur, Length());    // original uncompressed buffer length

   return 0;
}

//______________________________________________________________________________
Int_t TMessage::Uncompress()
{
   // Uncompress the message. The message will only be uncompressed when
   // kMESS_ZIP is set. Returns -1 in case of error, 0 otherwise.

   if (!fBufComp || !(fWhat & kMESS_ZIP))
      return -1;

   Int_t buflen;
   Int_t hdrlen = 2*sizeof(UInt_t);
   char *bufcur1 = fBufComp + hdrlen;
   frombuf(bufcur1, &buflen);
   UChar_t *bufcur = (UChar_t*)bufcur1;
   fBuffer  = new char[buflen];
   fBufSize = buflen;
   fBufCur  = fBuffer + sizeof(UInt_t) + sizeof(fWhat);
   fBufMax  = fBuffer + fBufSize;
   char *messbuf = fBuffer + hdrlen;

   Int_t nin, nout, nbuf;
   Int_t noutot = 0;
   while (1) {
      nin  = 9 + ((Int_t)bufcur[3] | ((Int_t)bufcur[4] << 8) | ((Int_t)bufcur[5] << 16));
      nbuf = (Int_t)bufcur[6] | ((Int_t)bufcur[7] << 8) | ((Int_t)bufcur[8] << 16);
      R__unzip(&nin, bufcur, &nbuf, messbuf, &nout);
      if (!nout) break;
      noutot += nout;
      if (noutot >= buflen - hdrlen) break;
      bufcur  += nin;
      messbuf += nout;
   }

   fWhat &= ~kMESS_ZIP;
   fCompress = 1;

   return 0;
}

//______________________________________________________________________________
void TMessage::WriteObject(const TObject *obj)
{
   // Write object to message buffer.
   // When support for schema evolution is enabled the list of TStreamerInfo
   // used to stream this object is kept in fInfos. This information is used
   // by TSocket::Send that sends this list through the socket. This list is in
   // turn used by TSocket::Recv to store the TStreamerInfo objects in the
   // relevant TClass in case the TClass does not know yet about a particular
   // class version. This feature is implemented to support clients and servers
   // with either different ROOT versions or different user classes versions.

   if (fgEvolution || fEvolution) {
      if (fInfos)
         fInfos->Clear();
      else
         fInfos = new TList();
   }

   fBitsPIDs.ResetAllBits();
   WriteObjectAny(obj, TObject::Class());
}

//______________________________________________________________________________
UShort_t TMessage::WriteProcessID(TProcessID *pid)
{
   // Check if the ProcessID pid is already in the message.
   // If not, then:
   //   - mark bit 0 of fBitsPIDs to indicate that a ProcessID has been found
   //   - mark bit uid+1 where uid id the uid of the ProcessID

   if (fBitsPIDs.TestBitNumber(0)) return 0;
   if (!pid)
      pid = TProcessID::GetPID();
   if (!pid) return 0;
   fBitsPIDs.SetBitNumber(0);
   UInt_t uid = pid->GetUniqueID();
   fBitsPIDs.SetBitNumber(uid+1);
   return 1;
}
<|endoftext|>


<commit_before><commit_msg>fixed gus unloading (worms would keep bad references to wpns/ninjarope otherwise)<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "name.h"

#include <algorithm>
#include <cstring>

// name - Naming Table
// http://www.microsoft.com/typography/otspec/name.htm

#define TABLE_NAME "name"

namespace {

bool ValidInPsName(char c) {
  return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c));
}

bool CheckPsNameAscii(const std::string& name) {
  for (unsigned i = 0; i < name.size(); ++i) {
    if (!ValidInPsName(name[i])) {
      return false;
    }
  }
  return true;
}

bool CheckPsNameUtf16Be(const std::string& name) {
  if ((name.size() & 1) != 0)
    return false;

  for (unsigned i = 0; i < name.size(); i += 2) {
    if (name[i] != 0) {
      return false;
    }
    if (!ValidInPsName(name[i+1])) {
      return false;
    }
  }
  return true;
}

void AssignToUtf16BeFromAscii(std::string* target,
                              const std::string& source) {
  target->resize(source.size() * 2);
  for (unsigned i = 0, j = 0; i < source.size(); i++) {
    (*target)[j++] = '\0';
    (*target)[j++] = source[i];
  }
}

}  // namespace

namespace ots {

bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
  Buffer table(data, length);

  OpenTypeNAME* name = new OpenTypeNAME;
  font->name = name;

  uint16_t format = 0;
  if (!table.ReadU16(&format) || format > 1) {
    return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format);
  }

  uint16_t count = 0;
  if (!table.ReadU16(&count)) {
    return OTS_FAILURE_MSG("Failed to read name count");
  }

  uint16_t string_offset = 0;
  if (!table.ReadU16(&string_offset) || string_offset > length) {
    return OTS_FAILURE_MSG("Failed to read strings offset");
  }
  const char* string_base = reinterpret_cast<const char*>(data) +
      string_offset;

  NameRecord prev_record;
  bool sort_required = false;

  // Read all the names, discarding any with invalid IDs,
  // and any where the offset/length would be outside the table.
  // A stricter alternative would be to reject the font if there
  // are invalid name records, but it's not clear that is necessary.
  for (unsigned i = 0; i < count; ++i) {
    NameRecord rec;
    uint16_t name_length, name_offset = 0;
    if (!table.ReadU16(&rec.platform_id) ||
        !table.ReadU16(&rec.encoding_id) ||
        !table.ReadU16(&rec.language_id) ||
        !table.ReadU16(&rec.name_id) ||
        !table.ReadU16(&name_length) ||
        !table.ReadU16(&name_offset)) {
      return OTS_FAILURE_MSG("Failed to read name entry %d", i);
    }
    // check platform & encoding, discard names with unknown values
    switch (rec.platform_id) {
      case 0:  // Unicode
        if (rec.encoding_id > 6) {
          continue;
        }
        break;
      case 1:  // Macintosh
        if (rec.encoding_id > 32) {
          continue;
        }
        break;
      case 2:  // ISO
        if (rec.encoding_id > 2) {
          continue;
        }
        break;
      case 3:  // Windows: IDs 7 to 9 are "reserved"
        if (rec.encoding_id > 6 && rec.encoding_id != 10) {
          continue;
        }
        break;
      case 4:  // Custom (OTF Windows NT compatibility)
        if (rec.encoding_id > 255) {
          continue;
        }
        break;
      default:  // unknown platform
        continue;
    }

    const unsigned name_end = static_cast<unsigned>(string_offset) +
        name_offset + name_length;
    if (name_end > length) {
      continue;
    }
    rec.text.resize(name_length);
    rec.text.assign(string_base + name_offset, name_length);

    if (rec.name_id == 6) {
      // PostScript name: check that it is valid, if not then discard it
      if (rec.platform_id == 1) {
        if (!CheckPsNameAscii(rec.text)) {
          continue;
        }
      } else if (rec.platform_id == 0 || rec.platform_id == 3) {
        if (!CheckPsNameUtf16Be(rec.text)) {
          continue;
        }
      }
    }

    if ((i > 0) && !(prev_record < rec)) {
      OTS_WARNING("name records are not sorted.");
      sort_required = true;
    }

    name->names.push_back(rec);
    prev_record = rec;
  }

  if (format == 1) {
    // extended name table format with language tags
    uint16_t lang_tag_count;
    if (!table.ReadU16(&lang_tag_count)) {
      return OTS_FAILURE_MSG("Failed to read language tag count");
    }
    for (unsigned i = 0; i < lang_tag_count; ++i) {
      uint16_t tag_length = 0;
      uint16_t tag_offset = 0;
      if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) {
        return OTS_FAILURE_MSG("Faile to read tag length or offset");
      }
      const unsigned tag_end = static_cast<unsigned>(string_offset) +
          tag_offset + tag_length;
      if (tag_end > length) {
        return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i);
      }
      std::string tag(string_base + tag_offset, tag_length);
      name->lang_tags.push_back(tag);
    }
  }

  if (table.offset() > string_offset) {
    // the string storage apparently overlapped the name/tag records;
    // consider this font to be badly broken
    return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset);
  }

  // check existence of required name strings (synthesize if necessary)
  //  [0 - copyright - skip]
  //   1 - family
  //   2 - subfamily
  //  [3 - unique ID - skip]
  //   4 - full name
  //   5 - version
  //   6 - postscript name
  static const uint16_t kStdNameCount = 7;
  static const char* kStdNames[kStdNameCount] = {
    NULL,
    "OTS derived font",
    "Unspecified",
    NULL,
    "OTS derived font",
    "1.000",
    "OTS-derived-font"
  };

  // scan the names to check whether the required "standard" ones are present;
  // if not, we'll add our fixed versions here
  bool mac_name[kStdNameCount] = { 0 };
  bool win_name[kStdNameCount] = { 0 };
  for (std::vector<NameRecord>::iterator name_iter = name->names.begin();
       name_iter != name->names.end(); name_iter++) {
    const uint16_t id = name_iter->name_id;
    if (id >= kStdNameCount || kStdNames[id] == NULL) {
      continue;
    }
    if (name_iter->platform_id == 1) {
      mac_name[id] = true;
      continue;
    }
    if (name_iter->platform_id == 3) {
      win_name[id] = true;
      continue;
    }
  }

  for (uint16_t i = 0; i < kStdNameCount; ++i) {
    if (kStdNames[i] == NULL) {
      continue;
    }
    if (!mac_name[i]) {
      NameRecord rec(1 /* platform_id */, 0 /* encoding_id */,
                     0 /* language_id */ , i /* name_id */);
      rec.text.assign(kStdNames[i]);
      name->names.push_back(rec);
      sort_required = true;
    }
    if (!win_name[i]) {
      NameRecord rec(3 /* platform_id */, 1 /* encoding_id */,
                     1033 /* language_id */ , i /* name_id */);
      AssignToUtf16BeFromAscii(&rec.text, std::string(kStdNames[i]));
      name->names.push_back(rec);
      sort_required = true;
    }
  }

  if (sort_required) {
    std::sort(name->names.begin(), name->names.end());
  }

  return true;
}

bool ots_name_should_serialise(Font *font) {
  return font->name != NULL;
}

bool ots_name_serialise(OTSStream* out, Font *font) {
  const OpenTypeNAME* name = font->name;

  uint16_t name_count = static_cast<uint16_t>(name->names.size());
  uint16_t lang_tag_count = static_cast<uint16_t>(name->lang_tags.size());
  uint16_t format = 0;
  size_t string_offset = 6 + name_count * 12;

  if (name->lang_tags.size() > 0) {
    // lang tags require a format-1 name table
    format = 1;
    string_offset += 2 + lang_tag_count * 4;
  }
  if (string_offset > 0xffff) {
    return OTS_FAILURE_MSG("Bad string offset %ld", string_offset);
  }
  if (!out->WriteU16(format) ||
      !out->WriteU16(name_count) ||
      !out->WriteU16(static_cast<uint16_t>(string_offset))) {
    return OTS_FAILURE_MSG("Failed to write name header");
  }

  std::string string_data;
  for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin();
       name_iter != name->names.end(); name_iter++) {
    const NameRecord& rec = *name_iter;
    if (string_data.size() + rec.text.size() >
            std::numeric_limits<uint16_t>::max() ||
        !out->WriteU16(rec.platform_id) ||
        !out->WriteU16(rec.encoding_id) ||
        !out->WriteU16(rec.language_id) ||
        !out->WriteU16(rec.name_id) ||
        !out->WriteU16(static_cast<uint16_t>(rec.text.size())) ||
        !out->WriteU16(static_cast<uint16_t>(string_data.size())) ) {
      return OTS_FAILURE_MSG("Faile to write name entry");
    }
    string_data.append(rec.text);
  }

  if (format == 1) {
    if (!out->WriteU16(lang_tag_count)) {
      return OTS_FAILURE_MSG("Faile to write language tag count");
    }
    for (std::vector<std::string>::const_iterator tag_iter =
             name->lang_tags.begin();
         tag_iter != name->lang_tags.end(); tag_iter++) {
      if (string_data.size() + tag_iter->size() >
              std::numeric_limits<uint16_t>::max() ||
          !out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
          !out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
        return OTS_FAILURE_MSG("Failed to write string");
      }
      string_data.append(*tag_iter);
    }
  }

  if (!out->Write(string_data.data(), string_data.size())) {
    return OTS_FAILURE_MSG("Faile to write string data");
  }

  return true;
}

void ots_name_reuse(Font *font, Font *other) {
  font->name = other->name;
  font->name_reused = true;
}

void ots_name_free(Font *font) {
  delete font->name;
}

}  // namespace

#undef TABLE_NAME
<commit_msg>[name] Don’t added synthesised entries needlessly<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "name.h"

#include <algorithm>
#include <cstring>

// name - Naming Table
// http://www.microsoft.com/typography/otspec/name.htm

#define TABLE_NAME "name"

namespace {

bool ValidInPsName(char c) {
  return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c));
}

bool CheckPsNameAscii(const std::string& name) {
  for (unsigned i = 0; i < name.size(); ++i) {
    if (!ValidInPsName(name[i])) {
      return false;
    }
  }
  return true;
}

bool CheckPsNameUtf16Be(const std::string& name) {
  if ((name.size() & 1) != 0)
    return false;

  for (unsigned i = 0; i < name.size(); i += 2) {
    if (name[i] != 0) {
      return false;
    }
    if (!ValidInPsName(name[i+1])) {
      return false;
    }
  }
  return true;
}

void AssignToUtf16BeFromAscii(std::string* target,
                              const std::string& source) {
  target->resize(source.size() * 2);
  for (unsigned i = 0, j = 0; i < source.size(); i++) {
    (*target)[j++] = '\0';
    (*target)[j++] = source[i];
  }
}

}  // namespace

namespace ots {

bool ots_name_parse(Font *font, const uint8_t* data, size_t length) {
  Buffer table(data, length);

  OpenTypeNAME* name = new OpenTypeNAME;
  font->name = name;

  uint16_t format = 0;
  if (!table.ReadU16(&format) || format > 1) {
    return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format);
  }

  uint16_t count = 0;
  if (!table.ReadU16(&count)) {
    return OTS_FAILURE_MSG("Failed to read name count");
  }

  uint16_t string_offset = 0;
  if (!table.ReadU16(&string_offset) || string_offset > length) {
    return OTS_FAILURE_MSG("Failed to read strings offset");
  }
  const char* string_base = reinterpret_cast<const char*>(data) +
      string_offset;

  NameRecord prev_record;
  bool sort_required = false;

  // Read all the names, discarding any with invalid IDs,
  // and any where the offset/length would be outside the table.
  // A stricter alternative would be to reject the font if there
  // are invalid name records, but it's not clear that is necessary.
  for (unsigned i = 0; i < count; ++i) {
    NameRecord rec;
    uint16_t name_length, name_offset = 0;
    if (!table.ReadU16(&rec.platform_id) ||
        !table.ReadU16(&rec.encoding_id) ||
        !table.ReadU16(&rec.language_id) ||
        !table.ReadU16(&rec.name_id) ||
        !table.ReadU16(&name_length) ||
        !table.ReadU16(&name_offset)) {
      return OTS_FAILURE_MSG("Failed to read name entry %d", i);
    }
    // check platform & encoding, discard names with unknown values
    switch (rec.platform_id) {
      case 0:  // Unicode
        if (rec.encoding_id > 6) {
          continue;
        }
        break;
      case 1:  // Macintosh
        if (rec.encoding_id > 32) {
          continue;
        }
        break;
      case 2:  // ISO
        if (rec.encoding_id > 2) {
          continue;
        }
        break;
      case 3:  // Windows: IDs 7 to 9 are "reserved"
        if (rec.encoding_id > 6 && rec.encoding_id != 10) {
          continue;
        }
        break;
      case 4:  // Custom (OTF Windows NT compatibility)
        if (rec.encoding_id > 255) {
          continue;
        }
        break;
      default:  // unknown platform
        continue;
    }

    const unsigned name_end = static_cast<unsigned>(string_offset) +
        name_offset + name_length;
    if (name_end > length) {
      continue;
    }
    rec.text.resize(name_length);
    rec.text.assign(string_base + name_offset, name_length);

    if (rec.name_id == 6) {
      // PostScript name: check that it is valid, if not then discard it
      if (rec.platform_id == 1) {
        if (!CheckPsNameAscii(rec.text)) {
          continue;
        }
      } else if (rec.platform_id == 0 || rec.platform_id == 3) {
        if (!CheckPsNameUtf16Be(rec.text)) {
          continue;
        }
      }
    }

    if ((i > 0) && !(prev_record < rec)) {
      OTS_WARNING("name records are not sorted.");
      sort_required = true;
    }

    name->names.push_back(rec);
    prev_record = rec;
  }

  if (format == 1) {
    // extended name table format with language tags
    uint16_t lang_tag_count;
    if (!table.ReadU16(&lang_tag_count)) {
      return OTS_FAILURE_MSG("Failed to read language tag count");
    }
    for (unsigned i = 0; i < lang_tag_count; ++i) {
      uint16_t tag_length = 0;
      uint16_t tag_offset = 0;
      if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) {
        return OTS_FAILURE_MSG("Faile to read tag length or offset");
      }
      const unsigned tag_end = static_cast<unsigned>(string_offset) +
          tag_offset + tag_length;
      if (tag_end > length) {
        return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i);
      }
      std::string tag(string_base + tag_offset, tag_length);
      name->lang_tags.push_back(tag);
    }
  }

  if (table.offset() > string_offset) {
    // the string storage apparently overlapped the name/tag records;
    // consider this font to be badly broken
    return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset);
  }

  // check existence of required name strings (synthesize if necessary)
  //  [0 - copyright - skip]
  //   1 - family
  //   2 - subfamily
  //  [3 - unique ID - skip]
  //   4 - full name
  //   5 - version
  //   6 - postscript name
  static const uint16_t kStdNameCount = 7;
  static const char* kStdNames[kStdNameCount] = {
    NULL,
    "OTS derived font",
    "Unspecified",
    NULL,
    "OTS derived font",
    "1.000",
    "OTS-derived-font"
  };

  // scan the names to check whether the required "standard" ones are present;
  // if not, we'll add our fixed versions here
  bool mac_name[kStdNameCount] = { 0 };
  bool win_name[kStdNameCount] = { 0 };
  for (std::vector<NameRecord>::iterator name_iter = name->names.begin();
       name_iter != name->names.end(); name_iter++) {
    const uint16_t id = name_iter->name_id;
    if (id >= kStdNameCount || kStdNames[id] == NULL) {
      continue;
    }
    if (name_iter->platform_id == 1) {
      mac_name[id] = true;
      continue;
    }
    if (name_iter->platform_id == 3) {
      win_name[id] = true;
      continue;
    }
  }

  for (uint16_t i = 0; i < kStdNameCount; ++i) {
    if (kStdNames[i] == NULL) {
      continue;
    }
    if (!mac_name[i] && !win_name[i]) {
      NameRecord mac_rec(1 /* platform_id */, 0 /* encoding_id */,
                         0 /* language_id */ , i /* name_id */);
      mac_rec.text.assign(kStdNames[i]);

      NameRecord win_rec(3 /* platform_id */, 1 /* encoding_id */,
                         1033 /* language_id */ , i /* name_id */);
      AssignToUtf16BeFromAscii(&win_rec.text, std::string(kStdNames[i]));

      name->names.push_back(mac_rec);
      name->names.push_back(win_rec);
      sort_required = true;
    }
  }

  if (sort_required) {
    std::sort(name->names.begin(), name->names.end());
  }

  return true;
}

bool ots_name_should_serialise(Font *font) {
  return font->name != NULL;
}

bool ots_name_serialise(OTSStream* out, Font *font) {
  const OpenTypeNAME* name = font->name;

  uint16_t name_count = static_cast<uint16_t>(name->names.size());
  uint16_t lang_tag_count = static_cast<uint16_t>(name->lang_tags.size());
  uint16_t format = 0;
  size_t string_offset = 6 + name_count * 12;

  if (name->lang_tags.size() > 0) {
    // lang tags require a format-1 name table
    format = 1;
    string_offset += 2 + lang_tag_count * 4;
  }
  if (string_offset > 0xffff) {
    return OTS_FAILURE_MSG("Bad string offset %ld", string_offset);
  }
  if (!out->WriteU16(format) ||
      !out->WriteU16(name_count) ||
      !out->WriteU16(static_cast<uint16_t>(string_offset))) {
    return OTS_FAILURE_MSG("Failed to write name header");
  }

  std::string string_data;
  for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin();
       name_iter != name->names.end(); name_iter++) {
    const NameRecord& rec = *name_iter;
    if (string_data.size() + rec.text.size() >
            std::numeric_limits<uint16_t>::max() ||
        !out->WriteU16(rec.platform_id) ||
        !out->WriteU16(rec.encoding_id) ||
        !out->WriteU16(rec.language_id) ||
        !out->WriteU16(rec.name_id) ||
        !out->WriteU16(static_cast<uint16_t>(rec.text.size())) ||
        !out->WriteU16(static_cast<uint16_t>(string_data.size())) ) {
      return OTS_FAILURE_MSG("Faile to write name entry");
    }
    string_data.append(rec.text);
  }

  if (format == 1) {
    if (!out->WriteU16(lang_tag_count)) {
      return OTS_FAILURE_MSG("Faile to write language tag count");
    }
    for (std::vector<std::string>::const_iterator tag_iter =
             name->lang_tags.begin();
         tag_iter != name->lang_tags.end(); tag_iter++) {
      if (string_data.size() + tag_iter->size() >
              std::numeric_limits<uint16_t>::max() ||
          !out->WriteU16(static_cast<uint16_t>(tag_iter->size())) ||
          !out->WriteU16(static_cast<uint16_t>(string_data.size()))) {
        return OTS_FAILURE_MSG("Failed to write string");
      }
      string_data.append(*tag_iter);
    }
  }

  if (!out->Write(string_data.data(), string_data.size())) {
    return OTS_FAILURE_MSG("Faile to write string data");
  }

  return true;
}

void ots_name_reuse(Font *font, Font *other) {
  font->name = other->name;
  font->name_reused = true;
}

void ots_name_free(Font *font) {
  delete font->name;
}

}  // namespace

#undef TABLE_NAME
<|endoftext|>


<commit_before>#include "network.h"

#include "server.h"
#include "client.h"
#include "gusgame.h"
#include "glua.h"
#include "gconsole.h"
#include "net_worm.h"
#include "game/WormInputHandler.h"
#include "particle.h"
#include "util/macros.h"
#include "util/log.h"
#include "util/text.h"
#include "lua/bindings-network.h"

#include <string>
#include <iostream>
#include <memory>
#include <list>
#include <set>
#include <utility>
#include "netstream.h"
#include <boost/assign/list_inserter.hpp>
using namespace boost::assign;

using namespace std;

int const Network::protocolVersion = 2;

LuaReference LuaEventDef::metaTable;

namespace
{

	mq_define_message(Connect, 0, ()) {}
	mq_end_define_message()

	Network::State state = Network::StateDisconnected;
	int stateTimeOut = 0;

	void setLuaState(Network::State s)
	{
		EACH_CALLBACK(i, networkStateChange) {
			(lua.call(*i), s)();
		}
	}

	void setState(Network::State s)
	{
		state = s;
	}

#define SET_STATE(s_) DLOG("Network state: " #s_); setState(State##s_)

	MessageQueue msg;

	struct LuaEventList
	{
		typedef std::map<std::string, LuaEventDef*> MapT;
		MapT stringToEvent;
		std::vector<LuaEventDef*> events;

		LuaEventDef* add(std::string const& name, LuaEventDef* event)
		{
			size_t idx = events.size() + 1;

			event->idx = idx;
			std::pair<MapT::iterator, bool> r = stringToEvent.insert(std::make_pair(name, event));
			if(!r.second) {
				delete event;
				event = r.first->second;
			}
			events.push_back(event);
			return event;
		}

		LuaEventDef* assign(std::string const& name, LuaEventDef* event)
		{
			std::pair<MapT::iterator, bool> r = stringToEvent.insert(std::make_pair(name, event));
			if(!r.second) {
				r.first->second->callb = event->callb;
				delete event;
				event = r.first->second;
			}
			return event;
		}

		void index(std::string const& name)
		{
			int idx = events.size() + 1;
			MapT::iterator i = stringToEvent.find(name);
			if(i != stringToEvent.end()) {
				i->second->idx = idx;
				events.push_back(i->second);
			} else
				events.push_back(0);
		}

		LuaEventDef* fromIndex(size_t idx)
		{
			if(idx > 0 && idx <= events.size())
				return events[idx - 1];
			return 0;
		}

		void clear()
		{
			stringToEvent.clear();
			events.clear();
		}

		void encode(Net_BitStream* data)
		{
			data->addInt(events.size(), 8);
			foreach(i, events) {
				DLOG("Encoding lua event: " << (*i)->name);
				data->addString((*i)->name.c_str());
			}
		}
	};

	std::string serverName;
	std::string serverDesc;
	bool m_host = false;
	bool m_client = false; //? This wasn't initialized before
	int logNetstream = 0; //TODO: Netstream
	std::set<Net_U32> bannedIPs;

	int m_serverPort; // Neither was this

	std::string m_lastServerAddr;
	int reconnectTimer = 0;
	int connCount = 0;

	Net_Control* m_control = 0;
	LuaEventList luaEvents[Network::LuaEventGroup::Max];

	void registerClasses() // Factorization of class registering in client and server
	{
		CWorm::classID = m_control->Net_registerClass("worm",Net_CLASSFLAG_ANNOUNCEDATA);
		CWormInputHandler::classID = m_control->Net_registerClass("player",Net_CLASSFLAG_ANNOUNCEDATA);
		GusGame::classID = m_control->Net_registerClass("gusGame",0);
		Particle::classID = m_control->Net_registerClass("particle",Net_CLASSFLAG_ANNOUNCEDATA);
	}

	std::string disconnectCmd(std::list<std::string> const& args)
	{
		return "Gusanos disconnect command not available";
	}
}

Network network;

void LuaEventDef::call(Net_BitStream* s)
{
	Net_BitStream* n = s->Duplicate();
	(lua.call(callb), luaReference, lua.fullReference(*n, LuaBindings::Net_BitStreamMetaTable))();
}

void LuaEventDef::call(LuaReference obj, Net_BitStream* s)
{
	Net_BitStream* n = s->Duplicate();
	(lua.call(callb), luaReference, obj, lua.fullReference(*n, LuaBindings::Net_BitStreamMetaTable))();
}

LuaEventDef::~LuaEventDef()
{
	lua.destroyReference(luaReference);
	luaReference.reset();
	lua.destroyReference(callb);
	callb.reset();
}

Network::Network()
{
}

Network::~Network()
{
}

void Network::log(char const* msg)
{
	cerr << "Gusanos Network: " << msg << endl;
}

void Network::init()
{
}

void Network::shutDown()
{
	delete m_control;
	m_control = 0;
}

void Network::registerInConsole()
{
	console.registerVariables()
	("NET_CHECK_CRC", &network.checkCRC, 1)
	;

}

Net_ConnID Network::getServerID()
{
	return NetConnID_server();
}

void Network::update()
{
	if ( m_control ) {
		m_control->Net_processOutput();
		m_control->Net_processInput();
	}

	switch(state) {
			case StateConnecting:
			case StateHosting:
			break;

			case StateDisconnected:
			case StateIdle:
			break;

			case StateDisconnecting: {
				if(connCount == 0 || stateTimeOut <= 0) {
					if(connCount != 0)
						WLOG(connCount << " connection(s) might not have disconnected properly.");
					setLuaState(StateDisconnected);
					SET_STATE(Disconnected);

					if(m_control) {
						m_control->Shutdown();
						delete m_control;
						m_control = 0;
					}

					connCount = 0;
					m_client = false;
					m_host = false;

					gusGame.removeNode();
				} else
					--stateTimeOut;
			}
			break;
	}

	if( reconnectTimer > 0 ) {
		//disconnect();
		if(--reconnectTimer == 0) {
			DLOG("Reconnecting Gusanos");
			olxConnect();
		}
	}
}

void Network::olxHost()
{
	if(state != StateDisconnected) { // We assume that we're disconnected
		errors << "Network::olxHost: state is not disconnected" << endl;
	}

	m_control = new Server();
	registerClasses();
	m_host = true;
	gusGame.assignNetworkRole( true ); // Gives the gusGame class node authority role
	setLuaState(StateHosting);
	SET_STATE(Idle);
}

void Network::olxConnect()
{
	// moved frmo Network::update() message queue handler to here:

	if(!isDisconnected()) {
		disconnect();

		// This would leave the Connect msg in the msg queue to do the connect as soon as we are disconnected
		//mq_delay(); // Wait until network is disconnected

		errors << "Network::olxConnect: we were not disconnected" << endl;
	}

	m_control = new Client();
	registerClasses();
	//m_client = true; // We wait with setting this until we've connected
	setLuaState(StateConnecting);
	SET_STATE(Idle);

	m_control->Net_ConnectToServer();
}

void Network::disconnect( DConnEvents event )
{
	if(state == StateIdle && m_control) {
		setLuaState(StateDisconnecting);
		SET_STATE(Disconnecting);
		stateTimeOut = 1000;

		Net_BitStream *eventData = new Net_BitStream;
		eventData->addInt( static_cast<int>( event ), 8 );

		LOG("Disconnecting...");
		m_control->Net_disconnectAll(eventData);
	}
}

void Network::disconnect( Net_ConnID id, DConnEvents event )
{
	if(!m_control)
		return;

	std::auto_ptr<Net_BitStream> eventData(new Net_BitStream);
	eventData->addInt( static_cast<int>( event ), 8 );
	m_control->Net_Disconnect( id, eventData.release());
}

void Network::clear()
{
	// The lua event tables contain pointers to objects allocated by lua
	for(int t = LuaEventGroup::GusGame; t < LuaEventGroup::Max; ++t) {
		luaEvents[t].clear();
	}
}

void Network::olxReconnect(int delay)
{
	reconnectTimer = delay;
}

bool Network::isHost()
{
	return m_host;
}

bool Network::isClient()
{
	return m_client;
}

Net_Control* Network::getNetControl()
{
	return m_control;
}

LuaEventDef* Network::addLuaEvent(LuaEventGroup::type type, const std::string& name, LuaEventDef* event)
{
	if(gusGame.options.host)
		return luaEvents[type].add(name, event);
	else if(m_client)
		return luaEvents[type].assign(name, event);

	return event;
}

void Network::indexLuaEvent(LuaEventGroup::type type, const std::string& name)
{
	if(m_client)
		luaEvents[type].index(name);
}

void Network::encodeLuaEvents(Net_BitStream* data)
{
	for(int t = LuaEventGroup::GusGame; t < LuaEventGroup::Max; ++t) {
		luaEvents[t].encode(data);
	}
}

LuaEventDef* Network::indexToLuaEvent(LuaEventGroup::type type, int idx)
{
	return luaEvents[type].fromIndex(idx);
}

void Network::incConnCount()
{
	++connCount;
}

void Network::decConnCount()
{
	--connCount;
}

bool Network::isDisconnected()
{
	return state == StateDisconnected;
}

bool Network::isDisconnecting()
{
	return state == StateDisconnecting;
}

void Network::setClient(bool v)
{
	m_client = v;
}

void Network::olxParse(Net_ConnID src, CBytestream& bs) {
	if(m_control)
		m_control->olxParse(src, bs);
	else {
		errors << "GusNetwork::olxParse: net control not initialised" << endl;
		bs.SkipAll();
	}
}

void Network::olxSend(bool sendPendingOnly) {
	// we can ignore Gusanos network in local play
	if(tLX->iGameType == GME_LOCAL) return;

	// if we don't use Gusanos at all, we don't need it
	if(!gusGame.isEngineNeeded()) return;

	if(m_control)
		m_control->olxSend(sendPendingOnly);
	else
		errors << "GusNetwork::olxSend: net control not initialised" << endl;
}

<commit_msg>fix: create gusgame client unique node<commit_after>#include "network.h"

#include "server.h"
#include "client.h"
#include "gusgame.h"
#include "glua.h"
#include "gconsole.h"
#include "net_worm.h"
#include "game/WormInputHandler.h"
#include "particle.h"
#include "util/macros.h"
#include "util/log.h"
#include "util/text.h"
#include "lua/bindings-network.h"

#include <string>
#include <iostream>
#include <memory>
#include <list>
#include <set>
#include <utility>
#include "netstream.h"
#include <boost/assign/list_inserter.hpp>
using namespace boost::assign;

using namespace std;

int const Network::protocolVersion = 2;

LuaReference LuaEventDef::metaTable;

namespace
{

	mq_define_message(Connect, 0, ()) {}
	mq_end_define_message()

	Network::State state = Network::StateDisconnected;
	int stateTimeOut = 0;

	void setLuaState(Network::State s)
	{
		EACH_CALLBACK(i, networkStateChange) {
			(lua.call(*i), s)();
		}
	}

	void setState(Network::State s)
	{
		state = s;
	}

#define SET_STATE(s_) DLOG("Network state: " #s_); setState(State##s_)

	MessageQueue msg;

	struct LuaEventList
	{
		typedef std::map<std::string, LuaEventDef*> MapT;
		MapT stringToEvent;
		std::vector<LuaEventDef*> events;

		LuaEventDef* add(std::string const& name, LuaEventDef* event)
		{
			size_t idx = events.size() + 1;

			event->idx = idx;
			std::pair<MapT::iterator, bool> r = stringToEvent.insert(std::make_pair(name, event));
			if(!r.second) {
				delete event;
				event = r.first->second;
			}
			events.push_back(event);
			return event;
		}

		LuaEventDef* assign(std::string const& name, LuaEventDef* event)
		{
			std::pair<MapT::iterator, bool> r = stringToEvent.insert(std::make_pair(name, event));
			if(!r.second) {
				r.first->second->callb = event->callb;
				delete event;
				event = r.first->second;
			}
			return event;
		}

		void index(std::string const& name)
		{
			int idx = events.size() + 1;
			MapT::iterator i = stringToEvent.find(name);
			if(i != stringToEvent.end()) {
				i->second->idx = idx;
				events.push_back(i->second);
			} else
				events.push_back(0);
		}

		LuaEventDef* fromIndex(size_t idx)
		{
			if(idx > 0 && idx <= events.size())
				return events[idx - 1];
			return 0;
		}

		void clear()
		{
			stringToEvent.clear();
			events.clear();
		}

		void encode(Net_BitStream* data)
		{
			data->addInt(events.size(), 8);
			foreach(i, events) {
				DLOG("Encoding lua event: " << (*i)->name);
				data->addString((*i)->name.c_str());
			}
		}
	};

	std::string serverName;
	std::string serverDesc;
	bool m_host = false;
	bool m_client = false; //? This wasn't initialized before
	int logNetstream = 0; //TODO: Netstream
	std::set<Net_U32> bannedIPs;

	int m_serverPort; // Neither was this

	std::string m_lastServerAddr;
	int reconnectTimer = 0;
	int connCount = 0;

	Net_Control* m_control = 0;
	LuaEventList luaEvents[Network::LuaEventGroup::Max];

	void registerClasses() // Factorization of class registering in client and server
	{
		CWorm::classID = m_control->Net_registerClass("worm",Net_CLASSFLAG_ANNOUNCEDATA);
		CWormInputHandler::classID = m_control->Net_registerClass("player",Net_CLASSFLAG_ANNOUNCEDATA);
		GusGame::classID = m_control->Net_registerClass("gusGame",0);
		Particle::classID = m_control->Net_registerClass("particle",Net_CLASSFLAG_ANNOUNCEDATA);
	}

	std::string disconnectCmd(std::list<std::string> const& args)
	{
		return "Gusanos disconnect command not available";
	}
}

Network network;

void LuaEventDef::call(Net_BitStream* s)
{
	Net_BitStream* n = s->Duplicate();
	(lua.call(callb), luaReference, lua.fullReference(*n, LuaBindings::Net_BitStreamMetaTable))();
}

void LuaEventDef::call(LuaReference obj, Net_BitStream* s)
{
	Net_BitStream* n = s->Duplicate();
	(lua.call(callb), luaReference, obj, lua.fullReference(*n, LuaBindings::Net_BitStreamMetaTable))();
}

LuaEventDef::~LuaEventDef()
{
	lua.destroyReference(luaReference);
	luaReference.reset();
	lua.destroyReference(callb);
	callb.reset();
}

Network::Network()
{
}

Network::~Network()
{
}

void Network::log(char const* msg)
{
	cerr << "Gusanos Network: " << msg << endl;
}

void Network::init()
{
}

void Network::shutDown()
{
	delete m_control;
	m_control = 0;
}

void Network::registerInConsole()
{
	console.registerVariables()
	("NET_CHECK_CRC", &network.checkCRC, 1)
	;

}

Net_ConnID Network::getServerID()
{
	return NetConnID_server();
}

void Network::update()
{
	if ( m_control ) {
		m_control->Net_processOutput();
		m_control->Net_processInput();
	}

	switch(state) {
			case StateConnecting:
			case StateHosting:
			break;

			case StateDisconnected:
			case StateIdle:
			break;

			case StateDisconnecting: {
				if(connCount == 0 || stateTimeOut <= 0) {
					if(connCount != 0)
						WLOG(connCount << " connection(s) might not have disconnected properly.");
					setLuaState(StateDisconnected);
					SET_STATE(Disconnected);

					if(m_control) {
						m_control->Shutdown();
						delete m_control;
						m_control = 0;
					}

					connCount = 0;
					m_client = false;
					m_host = false;

					gusGame.removeNode();
				} else
					--stateTimeOut;
			}
			break;
	}

	if( reconnectTimer > 0 ) {
		//disconnect();
		if(--reconnectTimer == 0) {
			DLOG("Reconnecting Gusanos");
			olxConnect();
		}
	}
}

void Network::olxHost()
{
	if(state != StateDisconnected) { // We assume that we're disconnected
		errors << "Network::olxHost: state is not disconnected" << endl;
	}

	m_control = new Server();
	registerClasses();
	m_host = true;
	gusGame.assignNetworkRole( true ); // Gives the gusGame class node authority role
	setLuaState(StateHosting);
	SET_STATE(Idle);
}

void Network::olxConnect()
{
	// moved frmo Network::update() message queue handler to here:

	if(!isDisconnected()) {
		disconnect();

		// This would leave the Connect msg in the msg queue to do the connect as soon as we are disconnected
		//mq_delay(); // Wait until network is disconnected

		errors << "Network::olxConnect: we were not disconnected" << endl;
	}

	m_control = new Client();
	registerClasses();
	//m_client = true; // We wait with setting this until we've connected
	setLuaState(StateConnecting);
	SET_STATE(Idle);

	m_control->Net_ConnectToServer();

	gusGame.assignNetworkRole( false );
}

void Network::disconnect( DConnEvents event )
{
	if(state == StateIdle && m_control) {
		setLuaState(StateDisconnecting);
		SET_STATE(Disconnecting);
		stateTimeOut = 1000;

		Net_BitStream *eventData = new Net_BitStream;
		eventData->addInt( static_cast<int>( event ), 8 );

		LOG("Disconnecting...");
		m_control->Net_disconnectAll(eventData);
	}
}

void Network::disconnect( Net_ConnID id, DConnEvents event )
{
	if(!m_control)
		return;

	std::auto_ptr<Net_BitStream> eventData(new Net_BitStream);
	eventData->addInt( static_cast<int>( event ), 8 );
	m_control->Net_Disconnect( id, eventData.release());
}

void Network::clear()
{
	// The lua event tables contain pointers to objects allocated by lua
	for(int t = LuaEventGroup::GusGame; t < LuaEventGroup::Max; ++t) {
		luaEvents[t].clear();
	}
}

void Network::olxReconnect(int delay)
{
	reconnectTimer = delay;
}

bool Network::isHost()
{
	return m_host;
}

bool Network::isClient()
{
	return m_client;
}

Net_Control* Network::getNetControl()
{
	return m_control;
}

LuaEventDef* Network::addLuaEvent(LuaEventGroup::type type, const std::string& name, LuaEventDef* event)
{
	if(gusGame.options.host)
		return luaEvents[type].add(name, event);
	else if(m_client)
		return luaEvents[type].assign(name, event);

	return event;
}

void Network::indexLuaEvent(LuaEventGroup::type type, const std::string& name)
{
	if(m_client)
		luaEvents[type].index(name);
}

void Network::encodeLuaEvents(Net_BitStream* data)
{
	for(int t = LuaEventGroup::GusGame; t < LuaEventGroup::Max; ++t) {
		luaEvents[t].encode(data);
	}
}

LuaEventDef* Network::indexToLuaEvent(LuaEventGroup::type type, int idx)
{
	return luaEvents[type].fromIndex(idx);
}

void Network::incConnCount()
{
	++connCount;
}

void Network::decConnCount()
{
	--connCount;
}

bool Network::isDisconnected()
{
	return state == StateDisconnected;
}

bool Network::isDisconnecting()
{
	return state == StateDisconnecting;
}

void Network::setClient(bool v)
{
	m_client = v;
}

void Network::olxParse(Net_ConnID src, CBytestream& bs) {
	if(m_control)
		m_control->olxParse(src, bs);
	else {
		errors << "GusNetwork::olxParse: net control not initialised" << endl;
		bs.SkipAll();
	}
}

void Network::olxSend(bool sendPendingOnly) {
	// we can ignore Gusanos network in local play
	if(tLX->iGameType == GME_LOCAL) return;

	// if we don't use Gusanos at all, we don't need it
	if(!gusGame.isEngineNeeded()) return;

	if(m_control)
		m_control->olxSend(sendPendingOnly);
	else
		errors << "GusNetwork::olxSend: net control not initialised" << endl;
}

<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <asio/ip/tcp.hpp>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout)
{
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"Connection: close\r\n"
		"\r\n\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout)
{
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error_code(), m_parser, 0, 0);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, 0, 0);
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<commit_msg>fix for rate limited http_connection<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/http_connection.hpp"

#include <boost/bind.hpp>
#include <asio/ip/tcp.hpp>

using boost::bind;

namespace libtorrent
{

void http_connection::get(std::string const& url, time_duration timeout)
{
	std::string protocol;
	std::string hostname;
	std::string path;
	int port;
	boost::tie(protocol, hostname, port, path) = parse_url_components(url);
	std::stringstream headers;
	headers << "GET " << path << " HTTP/1.0\r\n"
		"Host:" << hostname <<
		"Connection: close\r\n"
		"\r\n\r\n";
	sendbuffer = headers.str();
	start(hostname, boost::lexical_cast<std::string>(port), timeout);
}

void http_connection::start(std::string const& hostname, std::string const& port
	, time_duration timeout)
{
	m_timeout = timeout;
	m_timer.expires_from_now(m_timeout);
	m_timer.async_wait(bind(&http_connection::on_timeout
		, boost::weak_ptr<http_connection>(shared_from_this()), _1));
	m_called = false;
	if (m_sock.is_open() && m_hostname == hostname && m_port == port)
	{
		m_parser.reset();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
	else
	{
		m_sock.close();
		tcp::resolver::query query(hostname, port);
		m_resolver.async_resolve(query, bind(&http_connection::on_resolve
			, shared_from_this(), _1, _2));
		m_hostname = hostname;
		m_port = port;
	}
}

void http_connection::on_timeout(boost::weak_ptr<http_connection> p
	, asio::error_code const& e)
{
	if (e == asio::error::operation_aborted) return;
	boost::shared_ptr<http_connection> c = p.lock();
	if (!c) return;
	if (c->m_bottled && c->m_called) return;

	if (c->m_last_receive + c->m_timeout < time_now())
	{
		c->m_called = true;
		c->m_handler(asio::error::timed_out, c->m_parser, 0, 0);
		return;
	}

	c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
	c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
}

void http_connection::close()
{
	m_timer.cancel();
	m_limiter_timer.cancel();
	m_limiter_timer_active = false;
	m_sock.close();
	m_hostname.clear();
	m_port.clear();
}

void http_connection::on_resolve(asio::error_code const& e
		, tcp::resolver::iterator i)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}
	assert(i != tcp::resolver::iterator());
	m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
		, shared_from_this(), _1/*, ++i*/));
}

void http_connection::on_connect(asio::error_code const& e
	/*, tcp::resolver::iterator i*/)
{
	if (!e)
	{
		m_last_receive = time_now();
		asio::async_write(m_sock, asio::buffer(sendbuffer)
			, bind(&http_connection::on_write, shared_from_this(), _1));
	}
/*	else if (i != tcp::resolver::iterator())
	{
		// The connection failed. Try the next endpoint in the list.
		m_sock.close();
		m_sock.async_connect(*i, bind(&http_connection::on_connect
			, shared_from_this(), _1, ++i));
	}
*/	else
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
	}
}

void http_connection::on_write(asio::error_code const& e)
{
	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	std::string().swap(sendbuffer);
	m_recvbuffer.resize(4096);

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_read(asio::error_code const& e
	, std::size_t bytes_transferred)
{
	if (m_rate_limit)
	{
		m_download_quota -= bytes_transferred;
		assert(m_download_quota >= 0);
	}

	if (e == asio::error::eof)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error_code(), m_parser, 0, 0);
		return;
	}

	if (e)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(e, m_parser, 0, 0);
		return;
	}

	m_read_pos += bytes_transferred;
	assert(m_read_pos <= int(m_recvbuffer.size()));

	if (m_bottled || !m_parser.header_finished())
	{
		libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
			, &m_recvbuffer[0] + m_read_pos);
		m_parser.incoming(rcv_buf);
		if (!m_bottled && m_parser.header_finished())
		{
			if (m_read_pos > m_parser.body_start())
				m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
					, m_read_pos - m_parser.body_start());
			m_read_pos = 0;
			m_last_receive = time_now();
		}
		else if (m_bottled && m_parser.finished())
		{
			m_timer.cancel();
			if (m_bottled && m_called) return;
			m_called = true;
			m_handler(e, m_parser, 0, 0);
		}
	}
	else
	{
		assert(!m_bottled);
		m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
		m_read_pos = 0;
		m_last_receive = time_now();
	}

	if (int(m_recvbuffer.size()) == m_read_pos)
		m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500));
	if (m_read_pos == 1024 * 500)
	{
		close();
		if (m_bottled && m_called) return;
		m_called = true;
		m_handler(asio::error::eof, m_parser, 0, 0);
		return;
	}
	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (m_rate_limit > 0 && amount_to_read > m_download_quota)
	{
		amount_to_read = m_download_quota;
		if (m_download_quota == 0)
		{
			if (!m_limiter_timer_active)
				on_assign_bandwidth(asio::error_code());
			return;
		}
	}
	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));
}

void http_connection::on_assign_bandwidth(asio::error_code const& e)
{
	m_limiter_timer_active = false;
	if (e) return;

	if (m_download_quota > 0) return;

	m_download_quota = m_rate_limit / 4;

	int amount_to_read = m_recvbuffer.size() - m_read_pos;
	if (amount_to_read > m_download_quota)
		amount_to_read = m_download_quota;

	m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
		, amount_to_read)
		, bind(&http_connection::on_read
		, shared_from_this(), _1, _2));

	m_limiter_timer_active = true;
	m_limiter_timer.expires_from_now(milliseconds(250));
	m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
		, shared_from_this(), _1));
}

void http_connection::rate_limit(int limit)
{
	if (!m_limiter_timer_active)
	{
		m_limiter_timer_active = true;
		m_limiter_timer.expires_from_now(milliseconds(250));
		m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
			, shared_from_this(), _1));
	}
	m_rate_limit = limit;
}

}

<|endoftext|>


<commit_before>#include <samplog/Api.hpp>
#include "AmxDebugManager.hpp"
#include "Logger.hpp"
#include "LogManager.hpp"

#include "fmt/format.h"

class Api : public samplog::internal::IApi
{
public:
	void RegisterAmx(AMX *amx) override
	{
		AmxDebugManager::Get()->RegisterAmx(amx);
	}
	void EraseAmx(AMX *amx) override
	{
		AmxDebugManager::Get()->EraseAmx(amx);
	}

	bool GetLastAmxFunctionCall(AMX * const amx,
		samplog::AmxFuncCallInfo &destination) override
	{
		return AmxDebugManager::Get()->GetFunctionCall(amx, amx->cip, destination);
	}
	bool GetAmxFunctionCallTrace(AMX * const amx,
		std::vector<samplog::AmxFuncCallInfo> &dest) override
	{
		if (!AmxDebugManager::Get()->GetFunctionCallTrace(amx, dest))
			return false;

		return !dest.empty();
	}

	samplog::ILogger *CreateLogger(const char *module) override
	{
		if (strstr(module, "log-core") != nullptr)
			return nullptr;

		return new Logger(module);
	}
};

extern "C" DLL_PUBLIC samplog::internal::IApi *samplog_GetApi(int version)
{
	LogManager::Get(); // force init

	switch (version)
	{
	case 1:
		return new Api;
	default:
		LogManager::Get()->LogInternal(samplog::LogLevel::ERROR,
			fmt::format("unknown api version '{:d}'", version));
		break;
	}
	return nullptr;
}

extern "C" DLL_PUBLIC void samplog_DestroyApi(samplog::internal::IApi *api)
{
	if (api == nullptr)
		return;

	delete api;
}
<commit_msg>add api ref counter with singleton cleanup<commit_after>#include <samplog/Api.hpp>
#include "AmxDebugManager.hpp"
#include "Logger.hpp"
#include "LogManager.hpp"
#include "LogConfig.hpp"
#include "SampConfigReader.hpp"

#include "fmt/format.h"
#include <atomic>

std::atomic<unsigned int> RefCounter{ 0 };

class Api : public samplog::internal::IApi
{
public:
	void RegisterAmx(AMX *amx) override
	{
		AmxDebugManager::Get()->RegisterAmx(amx);
	}
	void EraseAmx(AMX *amx) override
	{
		AmxDebugManager::Get()->EraseAmx(amx);
	}

	bool GetLastAmxFunctionCall(AMX * const amx,
		samplog::AmxFuncCallInfo &destination) override
	{
		return AmxDebugManager::Get()->GetFunctionCall(amx, amx->cip, destination);
	}
	bool GetAmxFunctionCallTrace(AMX * const amx,
		std::vector<samplog::AmxFuncCallInfo> &dest) override
	{
		if (!AmxDebugManager::Get()->GetFunctionCallTrace(amx, dest))
			return false;

		return !dest.empty();
	}

	samplog::ILogger *CreateLogger(const char *module) override
	{
		if (strstr(module, "log-core") != nullptr)
			return nullptr;

		return new Logger(module);
	}
};

extern "C" DLL_PUBLIC samplog::internal::IApi *samplog_GetApi(int version)
{
	LogManager::Get(); // force init

	samplog::internal::IApi *api = nullptr;
	switch (version)
	{
	case 1:
		api = new Api;
		break;
	default:
		LogManager::Get()->LogInternal(samplog::LogLevel::ERROR,
			fmt::format("unknown api version '{:d}'", version));
		return nullptr;
	}

	RefCounter++;
	return api;
}

extern "C" DLL_PUBLIC void samplog_DestroyApi(samplog::internal::IApi *api)
{
	if (api == nullptr)
		return;

	delete api;
	RefCounter--;

	if (RefCounter == 0)
	{
		LogRotationManager::Destroy();
		SampConfigReader::Destroy();
		LogConfig::Destroy();
		LogManager::Destroy();
	}
}
<|endoftext|>