// This file is part of the ustl library, an STL implementation.
//
// Copyright (C) 2005 by Mike Sharov <msharov@users.sourceforge.net>
// This file is free software, distributed under the MIT License.
//
// mstream.cpp
//
//	Helper classes to read and write packed binary streams.
//

#include "mistream.h"
#include "sostream.h"
#include "ualgo.h"
#include "uassert.h"
#include "ustring.h"

#if PLATFORM_ANDROID
#include <stdio.h>
#endif

namespace ustl {

//--------------------------------------------------------------------

/// \brief Constructs a stream attached to nothing.
/// A stream attached to nothing is not usable. Call Link() functions
/// inherited from cmemlink to attach to some memory block.
///
istream::istream (void)
: cmemlink (),
  m_Pos (0)
{
}

/// Attaches the stream to a block at \p p of size \p n.
istream::istream (const void* p, size_type n)
: cmemlink (p, n),
  m_Pos (0)
{
}

/// Attaches to the block pointed to by \p source.
istream::istream (const cmemlink& source)
: cmemlink (source),
  m_Pos (0)
{
}

/// Attaches to the block pointed to by source of size source.pos()
istream::istream (const ostream& source)
: cmemlink (source.begin(), source.pos()),
  m_Pos (0)
{
}

/// Swaps contents with \p is
void istream::swap (istream& is)
{
    cmemlink::swap (is);
    ::ustl::swap (m_Pos, is.m_Pos);
}

/// Checks that \p n bytes are available in the stream, or else throws.
void istream::verify_remaining (const char* op, const char* type, size_t n) const
{
    if (remaining() < n)
#if PLATFORM_ANDROID
        printf("stream bounds exception\n");
#else
	throw stream_bounds_exception (op, type, pos(), n, remaining());
#endif
}

/// Reads \p n bytes into \p buffer.
void istream::read (void* buffer, size_type n)
{
#ifdef WANT_STREAM_BOUNDS_CHECKING
    verify_remaining ("read", "binary data", n);
#else
    assert (remaining() >= n && "Reading past end of buffer. Make sure you are reading the right format.");
#endif
    copy_n (ipos(), n, reinterpret_cast<value_type*>(buffer));
    m_Pos += n;
}

/// Reads a null-terminated string into \p str.
void istream::read_strz (string& str)
{
    const_iterator zp = find (ipos(), end(), string::c_Terminator);
    if (zp == end())
	zp = ipos();
    const size_type strl = distance (ipos(), zp);
    str.resize (strl);
    copy (ipos(), zp, str.begin());
    m_Pos += strl + 1;
}

/// Reads at most \p n bytes into \p s.
istream::size_type istream::readsome (void* s, size_type n)
{
    if (remaining() < n)
	underflow (n);
    const size_type ntr (min (n, remaining()));
    read (s, ntr);
    return (ntr);
}

/// Writes all unread bytes into \p os.
void istream::write (ostream& os) const
{
    os.write (ipos(), remaining());
}

/// Writes the object to stream \p os.
void istream::text_write (ostringstream& os) const
{
    os.write (ipos(), remaining());
}

/// Links to \p p of size \p n
void istream::unlink (void)
{
    cmemlink::unlink();
    m_Pos = 0;
}

//--------------------------------------------------------------------

/// \brief Constructs a stream attached to nothing.
/// A stream attached to nothing is not usable. Call Link() functions
/// inherited from memlink to attach to some memory block.
///
ostream::ostream (void)
: memlink (),
  m_Pos (0)
{
}

/// Attaches the stream to a block at \p p of size \p n.
ostream::ostream (void* p, size_type n)
: memlink (p, n),
  m_Pos (0)
{
}

/// Attaches to the block pointed to by \p source.
ostream::ostream (const memlink& source)
: memlink (source),
  m_Pos (0)
{
}

/// Links to \p p of size \p n
void ostream::unlink (void)
{
    memlink::unlink();
    m_Pos = 0;
}

/// Checks that \p n bytes are available in the stream, or else throws.
void ostream::verify_remaining (const char* op, const char* type, size_t n) const
{
    if (remaining() < n)
#if PLATFORM_ANDROID
        printf("stream bounds exception\n");
#else
	throw stream_bounds_exception (op, type, pos(), n, remaining());
#endif
}

/// Aligns the write pointer on \p grain. The skipped bytes are zeroed.
void ostream::align (size_type grain)
{
    const size_t r = pos() % grain;
    size_t nb = grain - r;
    if (!r) nb = 0;
#ifdef WANT_STREAM_BOUNDS_CHECKING
    verify_remaining ("align", "padding", nb);
#else
    assert (remaining() >= nb && "Buffer overrun. Check your stream size calculations.");
#endif
    fill_n (ipos(), nb, '\x0');
    m_Pos += nb;
}

/// Writes \p n bytes from \p buffer.
void ostream::write (const void* buffer, size_type n)
{
#ifdef WANT_STREAM_BOUNDS_CHECKING
    verify_remaining ("write", "binary data", n);
#else
    assert (remaining() >= n && "Buffer overrun. Check your stream size calculations.");
#endif
    copy_n (const_iterator(buffer), n, ipos());
    m_Pos += n;
}

/// Writes \p str as a null-terminated string.
void ostream::write_strz (const char* str)
{
    write (str, strlen(str));
    iwrite (string::c_Terminator);
}

/// Writes all available data from \p is.
void ostream::read (istream& is)
{
    is.write (*this);
    is.seek (is.size());
}

/// Writes all written data to \p os.
void ostream::text_write (ostringstream& os) const
{
    os.write (begin(), pos());
}

/// Inserts an empty area of \p size, at \p start.
void ostream::insert (iterator start, size_type s)
{
    memlink::insert (start, s);
    m_Pos += s;
}

/// Erases an area of \p size, at \p start.
void ostream::erase (iterator start, size_type s)
{
    m_Pos -= s;
    memlink::erase (start, s);
}

/// Swaps with \p os
void ostream::swap (ostream& os)
{
    memlink::swap (os);
    ::ustl::swap (m_Pos, os.m_Pos);
}

//--------------------------------------------------------------------

} // namespace ustl