/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- hex2digit
- hex2mem
- hexify
- hexify
- hexify
- init
- copy
- append
- remove
- remove
- remove
- clear
- resize
- resize
- dump
- corrupt
- corrupt
//
// Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
#ifdef HAVE_CONFIG_H
#include "gnashconfig.h"
#endif
#include <boost/cstdint.hpp>
#include <iostream>
#include <boost/random/uniform_int.hpp>
#include <boost/random/mersenne_twister.hpp>
#include "buffer.h"
#include "amf.h"
#include "log.h"
//#include "network.h"
#include "GnashException.h"
/// \namespace cygnal
///
/// This namespace is for all the AMF specific classes in libamf.
namespace cygnal
{
/// \brief Convert a Hex digit into it's decimal value.
///
/// @param digit The digit as a hex value
///
/// @return The byte as a decimal value.
boost::uint8_t
Buffer::hex2digit (boost::uint8_t digit)
{
if (digit == 0)
return 0;
if (digit >= '0' && digit <= '9')
return digit - '0';
if (digit >= 'a' && digit <= 'f')
return digit - 'a' + 10;
if (digit >= 'A' && digit <= 'F')
return digit - 'A' + 10;
// shouldn't ever get this far
return -1;
}
/// \brief Encode a Buffer from a hex string.
///
/// @param str A hex string, ex... "00 03 05 0a"
///
/// @return A reference to a Buffer in host endian format. This is
/// primary used only for testing to create binary data
/// from an easy to read and edit format.
Buffer &
Buffer::hex2mem(const std::string &str)
{
// GNASH_REPORT_FUNCTION;
size_t count = str.size();
size_t size = (count/3) + 4;
boost::uint8_t ch = 0;
boost::uint8_t *ptr = const_cast<boost::uint8_t *>(reinterpret_cast<const boost::uint8_t *>(str.c_str()));
boost::uint8_t *end = ptr + count;
init(size);
for (size_t i=0; ptr<end; i++) {
if (*ptr == ' ') { // skip spaces.
ptr++;
continue;
}
ch = hex2digit(*ptr++) << 4;
ch |= hex2digit(*ptr++);
*this += ch;
i++;
}
resize(size);
return *this;
}
// Convert each byte into its hex representation
std::string
Buffer::hexify ()
{
return gnash::hexify(_data.get(), allocated(), false);
}
std::string
Buffer::hexify (bool ascii)
{
return gnash::hexify(_data.get(), allocated(), ascii);
}
std::string
Buffer::hexify (cygnal::Buffer &buf, bool ascii)
{
return gnash::hexify(buf.reference(), buf.allocated(), ascii);
}
/// \brief Initialize a block of memory for this buffer.
/// This should only be used internally by the Buffer
/// class.
///
/// @param nbytes The total size to allocate memory for.
///
/// @return A reference to the initialized Buffer.
Buffer &
Buffer::init(size_t size)
{
// GNASH_REPORT_FUNCTION;
if (!_data) {
_data.reset(new boost::uint8_t[size]);
_seekptr = _data.get();
}
_seekptr = _data.get();
_nbytes = size;
clear();// FIXME; this is a perforance hit, but aids in debugging
#ifdef USE_STATS_BUFFERS
clock_gettime (CLOCK_REALTIME, &_stamp);
#endif
return *this;
}
/// \brief Create a new Buffer with the default size
Buffer::Buffer()
: _seekptr(0)
{
// GNASH_REPORT_FUNCTION;
_nbytes = cygnal::NETBUFSIZE;
init(cygnal::NETBUFSIZE);
}
/// \brief Create a new Buffer with a size other than the default
Buffer::Buffer(size_t nbytes)
: _seekptr(0)
{
// GNASH_REPORT_FUNCTION;
_nbytes = nbytes;
init(_nbytes);
}
/// \brief Create a new Buffer with a hex string.
/// This is primary used only for testing to create binary
/// data from an easy to read and edit format.
/// @param str A hex string, ex... "00 03 05 0a"
Buffer::Buffer(const std::string &str)
{
// GNASH_REPORT_FUNCTION;
hex2mem(str);
}
/// Delete the memory allocated for this Buffer
Buffer::~Buffer()
{
// GNASH_REPORT_FUNCTION;
if (_data) {
#ifdef USE_STATS_BUFFERS
struct timespec now;
clock_gettime (CLOCK_REALTIME, &now);
log_debug("Buffer %x (%d) stayed in queue for %f seconds",
(void *)_data.get(), _nbytes,
(float)((now.tv_sec - _stamp.tv_sec) + ((now.tv_nsec - _stamp.tv_nsec)/1e9)));
#endif
_seekptr = 0;
_nbytes = 0;
}
}
/// \brief Copy data into the buffer.
/// This overwrites all data, and resets the seek ptr.
///
/// @param data A pointer to the raw bytes to copy into the
/// buffer.
///
/// @param nbytes The number of bytes to copy.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::copy(boost::uint8_t *data, size_t nbytes)
{
// GNASH_REPORT_FUNCTION;
if (_data) {
if (_nbytes >= nbytes) {
std::copy(data, data + nbytes, _data.get());
_seekptr = _data.get() + nbytes;
} else {
boost::format msg("Not enough storage was allocated to hold the "
"copied data! Needs %1%, only has %2% bytes");
msg % nbytes % _nbytes;
throw gnash::GnashException(msg.str());
}
}
return *this;
}
/// \brief Append data to existing data in the buffer.
///
/// @param data A pointer to the raw bytes to append to the
/// buffer.
///
/// @param nbytes The number of bytes to append.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::append(boost::uint8_t *data, size_t nbytes)
{
// GNASH_REPORT_FUNCTION;
if (_data) {
if (spaceLeft() >= nbytes) {
std::copy(data, data + nbytes, _seekptr);
_seekptr += nbytes;
} else {
boost::format msg("Not enough storage was allocated to hold the "
"appended data! Needs %1%, only has %2% bytes");
msg % nbytes % spaceLeft();
throw gnash::GnashException(msg.str());
}
}
return *this;
}
/// \brief Append a Buffer class to existing data in the buffer.
///
/// @param buf A Buffer class containing the data to append.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(Buffer &buf)
{
// // GNASH_REPORT_FUNCTION;
append(buf.reference(), buf.allocated());
return *this;
}
/// \brief Copy a AMF0 type into the buffer.
/// This overwrites all data, and resets the seek ptr.
///
/// @param type An AMF0 type.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(cygnal::Element::amf0_type_e type)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t nb = static_cast<boost::uint8_t>(type);
return operator+=(nb);
}
/// \brief Append a byte to existing data in the buffer.
///
/// @param byte A single byte.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(char byte)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t nb = static_cast<boost::uint8_t>(byte);
return operator+=(nb);
}
/// \brief Append a boolean to existing data in the buffer.
///
/// @param type A boolean.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(bool flag)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t nb = static_cast<boost::uint8_t>(flag);
return operator+=(nb);
}
/// \brief Append a byte to existing data in the buffer.
///
/// @param byte A single byte.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(boost::uint8_t byte)
{
// GNASH_REPORT_FUNCTION;
if ((_seekptr + 1) <= (_data.get() + _nbytes)) {
*_seekptr = byte;
_seekptr += sizeof(boost::uint8_t);
}
return *this;
}
/// \brief Append a string to existing data in the buffer.
///
/// @param str A string containing ASCII data to copy into the
/// buffer.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(const char *str)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t *ptr = const_cast<boost::uint8_t *>(reinterpret_cast<const boost::uint8_t *>(str));
return append(ptr, strlen(str));
}
/// \brief Append a string to existing data in the buffer.
///
/// @param str A string containing ASCII data to copy into the
/// buffer.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(const std::string &str)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t *ptr = const_cast<boost::uint8_t *>(reinterpret_cast<const boost::uint8_t *>(str.c_str()));
return append(ptr, str.size());
}
/// \brief Append a double to existing data in the buffer.
///
/// @param num A numeric double value.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(double num)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t *ptr = reinterpret_cast<boost::uint8_t *>(&num);
return append(ptr, AMF0_NUMBER_SIZE);
}
/// \brief Append a short to existing data in the buffer.
///
/// @param num A numeric short value.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(boost::uint16_t length)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t *ptr = reinterpret_cast<boost::uint8_t *>(&length);
return append(ptr, sizeof(boost::uint16_t));
}
/// \brief Append an integer to existing data in the buffer.
///
/// @param num A numeric integer value.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(boost::uint32_t length)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t *ptr = reinterpret_cast<boost::uint8_t *>(&length);
return append(ptr, sizeof(boost::uint32_t));
}
/// \brief Append a Buffer class to existing data in the buffer.
///
/// @param buf A Buffer class containing the data to append.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator+=(boost::shared_ptr<Buffer> &buf)
{
// GNASH_REPORT_FUNCTION;
append(buf->reference(), buf->allocated());
return *this;
}
/// \brief Append a Buffer class to existing data in the buffer.
///
/// @param buf A Buffer class containing the data to append.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator=(Buffer &buf)
{
// GNASH_REPORT_FUNCTION;
if (buf.size() != _nbytes) {
resize(buf.size());
}
copy(buf.reference(), buf.size());
return *this;
}
/// \brief Copy a string into the buffer.
/// This overwrites all data, and resets the seek ptr.
///
/// @param str A string containing ASCII data to copy into the
/// buffer.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator=(const std::string &str)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t *ptr = const_cast<boost::uint8_t *>(reinterpret_cast<const boost::uint8_t *>(str.c_str()));
return copy(ptr, str.size());
}
Buffer &
Buffer::operator=(const char *str)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t *ptr = const_cast<boost::uint8_t *>(reinterpret_cast<const boost::uint8_t *>(str));
return copy(ptr, strlen(str));
}
/// \brief Copy a double into the buffer.
/// This overwrites all data, and resets the seek ptr.
///
/// @param num A numeric double value.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator=(double num)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t *ptr = reinterpret_cast<boost::uint8_t *>(&num);
return copy(ptr, AMF0_NUMBER_SIZE);
}
/// \brief Copy a short into the buffer.
/// This overwrites all data, and resets the seek ptr.
///
/// @param num A numeric short value.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator=(boost::uint16_t length)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t *ptr = reinterpret_cast<boost::uint8_t *>(&length);
return copy(ptr, sizeof(boost::uint16_t));
}
/// \brief Copy a AMF0 type into the buffer.
/// This overwrites all data, and resets the seek ptr.
///
/// @param type An AMF0 type.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator=(cygnal::Element::amf0_type_e type)
{
boost::uint8_t nb = static_cast<boost::uint8_t>(type);
return operator=(nb);
}
/// Copy a boolean into the buffer. This overwrites all data, and
/// resets the seek ptr.
///
/// @param flag A boolean.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator=(bool flag)
{
boost::uint8_t nb = static_cast<boost::uint8_t>(flag);
return operator=(nb);
}
/// \brief Copy a byte into the buffer.
/// This overwrites all data, and resets the seek ptr.
///
/// @param byte A single byte.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator=(boost::uint8_t byte)
{
// GNASH__FUNCTION;
return copy(&byte, 1);
}
/// \brief Copy a byte into the buffer.
/// This overwrites all data, and resets the seek ptr.
///
/// @param byte A pointer to a single byte.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator=(boost::uint8_t *data)
{
// GNASH_REPORT_FUNCTION;
if (data) {
_data.reset(data);
} else {
throw gnash::ParserException("Passing invalid pointer!");
}
return *this;
}
/// \brief Copy a Buffer class into the buffer.
/// This overwrites all data, and resets the seek ptr.
///
/// @param buf A Buffer class containing the data to copy.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::operator=(boost::shared_ptr<Buffer> &buf)
{
// GNASH_REPORT_FUNCTION;
copy(buf->reference(), buf->size());
return *this;
}
/// \brief Test equivalance against another Buffer.
/// This compares all the data on the current Buffer with
/// the supplied one, so it can be a performance hit. This
/// is primarily only used for testing purposes.
///
/// @param buf A reference to a Buffer.
///
/// @return A boolean true if the Buffers are indentical.
bool
Buffer::operator==(Buffer &buf)
{
// GNASH_REPORT_FUNCTION;
if (buf.size() == _nbytes){
if (memcmp(buf.reference(), _data.get(), _nbytes) == 0) {
return true;
}
}
return false;
}
/// \brief Drop a byte without resizing.
/// This will remove the byte from the Buffer, and then
/// move the remaining data to be in the correct
/// location. This resets the seek pointer.
///
/// @param byte The byte to remove from the buffer.
///
/// @return A real pointer to the base address of the buffer.
boost::uint8_t *
Buffer::remove(boost::uint8_t c)
{
// GNASH_REPORT_FUNCTION;
boost::uint8_t *start = std::find(begin(), end(), c);
// log_debug("Byte is at %x", (void *)start);
if (start == 0) {
return 0;
}
std::copy(start + 1, end(), start);
*(end() - 1) = 0;
_seekptr--;
return _data.get();
}
/// \brief Drop a byte without resizing.
/// This will remove the byte from the Buffer, and then
/// move the remaining data to be in the correct
/// location. This resets the seek pointer.
///
/// @param start The location of the byte to remove from the
/// Buffer
///
/// @return A real pointer to the base address of the Buffer.
boost::uint8_t *
Buffer::remove(int start)
{
// GNASH_REPORT_FUNCTION;
std::copy((_data.get() + start + 1), end(), (_data.get() + start)),
// *end() = 0;
_seekptr--;
return _data.get();
}
/// \brief Drop bytes without resizing.
/// This will remove the bytes from the Buffer, and then
/// move the remaining data to be in the correct
/// location. This resets the seek pointer.
///
/// @param index The location of the byte to start removing data
/// from the Buffer. This is an numerical value, not a
/// pointer.
///
/// @param start The location of the byte to remove from the
/// Buffer
/// @param range The amount of bytes to remove from the Buffer.
///
/// @return A real pointer to the base address of the Buffer.
boost::uint8_t *
Buffer::remove(int start, int range)
{
// GNASH_REPORT_FUNCTION;
std::copy((_data.get() + range + 1), end(), (_data.get() + start)),
// *end() = 0;
_seekptr -= range;
return _data.get();
}
/// \brief Clear the contents of the buffer by setting all the bytes to
/// zeros.
///
/// @return nothing
void
Buffer::clear()
{
// GNASH_REPORT_FUNCTION;
if (_data) {
memset(_data.get(), 0, _nbytes);
}
_seekptr = _data.get();
}
/// \brief Resize the buffer that holds the data.
/// The new size of the current data is based on the
/// current amount of data within the allocated memory.
/// This is used to make a Buffer the same size as
/// the existing data, and to truncate the unsed portion
/// of the Buffer when copying to the new memory
/// location.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::resize()
{
// GNASH_REPORT_FUNCTION;
return resize(_seekptr - _data.get());
}
/// \brief Resize the buffer that holds the data.
/// If the size is larger than the existing data, the data
/// is copied into the new region. If the size is smaller
/// than the existing data, the remaining data is
/// truncated when copying to the new memory location.
///
/// @param nbyte The size to resize the Buffer to.
///
/// @return A reference to a Buffer.
Buffer &
Buffer::resize(size_t size)
{
// GNASH_REPORT_FUNCTION;
boost::scoped_array<boost::uint8_t> tmp;
// If there is no size, don't do anything
if (size == 0) {
return *this;
}
// If we don't have any data yet in this buffer, resizing is cheap, as
// we don't havce to copy any data.
if (_seekptr == _data.get()) {
_data.reset(new boost::uint8_t[size]);
_nbytes= size;
return *this;
}
if (_nbytes == 0) {
return init(size);
} else {
// Don't bother to resize without really changing anything
if (size == _nbytes) {
return *this;
}
// check the sizes. If we had data read using ->reference(), the seekptr isn't
// increased, so in these cases we just copy al lthe data blindly, as it's
// better than loosing data.
size_t used = 0;
if (_seekptr != _data.get()) {
used = _seekptr - _data.get();
} else {
if (size < _nbytes) {
used = size;
} else {
used = _nbytes;
}
}
// Copy the existing data into the new block of memory. The data
// held currently is moved to the temporary array, and then gets
// deleted when this method returns.
// We loose data if we resize smaller than the data currently held.
if (size < used) {
gnash::log_error("cygnal::Buffer::resize(%d): Truncating data (%d bytes) while resizing!", size, used - size);
used = size;
}
boost::uint8_t *newptr = new boost::uint8_t[size];
std::copy(_data.get(), _data.get() + used, newptr);
_data.reset(newptr);
// Make the seekptr point into the new space with the correct offset
_seekptr = _data.get() + used;
// Adjust the size
_nbytes = size;
}
return *this;
}
/// \brief Dump the internal data of this class in a human readable form.
/// This should only be used for debugging purposes.
void
Buffer::dump(std::ostream& os) const
{
os << "Buffer is " << _seekptr-_data.get() << "/" << _nbytes << " bytes: ";
// Skip in-memory address " at " << (void *)_data.get() << endl;
if (_nbytes > 0) {
const size_t bytes = _seekptr - _data.get();
os << gnash::hexify((unsigned char *)_data.get(), bytes, false)
<< std::endl;
os << gnash::hexify((unsigned char *)_data.get(), bytes, true)
<< std::endl;
} else {
os << "ERROR: Buffer size out of range!" << std::endl;
}
}
/// \brief Corrupt a buffer with random errors.
/// This is used only for testing to make sure we can cleanly
/// handle corruption of the packets.
///
/// @param factor A divisor to adjust how many errors are created.
///
/// @return nothing
int
Buffer::corrupt()
{
return corrupt(10);
}
int
Buffer::corrupt(int factor)
{
boost::mt19937 seed;
// Pick the number of errors to create based on the Buffer's data size
boost::uniform_int<> errs(1, (_nbytes/factor));
int errors = errs(seed);
gnash::log_debug("Creating %d errors in the buffer", errors);
for (int i=0; i<errors; i++) {
// find a location someplace within the file.
boost::uniform_int<> location(0, _nbytes);
int pos = location(seed);
// log_debug("Creating error at %d in the buffer", pos);
// Create a random new value for the byte
boost::uniform_int<> shift(1, 256);
int newval = shift(seed);
// stomp the old value for our new one.
_data[pos] = newval;
}
return errors;
}
} // end of amf namespace
// local Variables:
// mode: C++
// indent-tabs-mode: t
// End: