PageRenderTime 233ms CodeModel.GetById 90ms app.highlight 71ms RepoModel.GetById 70ms app.codeStats 0ms

/mordor/streams/memory.cpp

http://github.com/mozy/mordor
C++ | 199 lines | 167 code | 21 blank | 11 comment | 29 complexity | 417c411c02010732ef71742da4f14fc8 MD5 | raw file
  1// Copyright (c) 2009 - Mozy, Inc.
  2
  3#include "memory.h"
  4
  5#include <stdexcept>
  6
  7#include "mordor/assert.h"
  8
  9namespace Mordor {
 10
 11MemoryStream::MemoryStream()
 12: m_offset(0)
 13{}
 14
 15MemoryStream::MemoryStream(const Buffer &b)
 16: m_read(b),
 17  m_original(b),
 18  m_offset(0)
 19{}
 20
 21size_t
 22MemoryStream::read(Buffer &buffer, size_t length)
 23{
 24    return readInternal(buffer, length);
 25}
 26
 27size_t
 28MemoryStream::read(void *buffer, size_t length)
 29{
 30    return readInternal(buffer, length);
 31}
 32
 33template <class T>
 34size_t
 35MemoryStream::readInternal(T &buffer, size_t length)
 36{
 37    size_t todo = (std::min)(length, m_read.readAvailable());
 38    m_read.copyOut(buffer, todo);
 39    m_read.consume(todo);
 40    m_offset += todo;
 41    return todo;
 42}
 43
 44size_t
 45MemoryStream::write(const Buffer &buffer, size_t length)
 46{
 47    return writeInternal(buffer, length);
 48}
 49
 50size_t
 51MemoryStream::write(const void *buffer, size_t length)
 52{
 53    return writeInternal(buffer, length);
 54}
 55
 56template <class T>
 57size_t
 58MemoryStream::writeInternal(const T &buffer, size_t length)
 59{
 60    size_t size = m_original.readAvailable();
 61    if (m_offset == size) {
 62        m_original.copyIn(buffer, length);
 63        m_offset += length;
 64    } else if (m_offset > size) {
 65        // extend the stream, then write
 66        truncate(m_offset);
 67        m_original.copyIn(buffer, length);
 68        m_offset += length;
 69    } else {
 70        // write at offset
 71        Buffer original(m_original);
 72        // Truncate original to all data before the write
 73        m_original.truncate(m_offset);
 74        original.consume(m_offset);
 75        // copy in the write, advancing the stream pointer
 76        m_original.copyIn(buffer, length);
 77        m_offset += length;
 78        if (m_offset < size) {
 79            original.consume(length);
 80            // Copy in any remaining data beyond the write
 81            m_original.copyIn(original);
 82            // Reset our read buffer to the current stream pos
 83            m_read.clear();
 84            m_read.copyIn(original);
 85        }
 86    }
 87    return length;
 88}
 89
 90long long
 91MemoryStream::seek(long long offset, Anchor anchor)
 92{
 93    size_t size = m_original.readAvailable();
 94
 95    switch (anchor) {
 96        case BEGIN:
 97            if (offset < 0)
 98                MORDOR_THROW_EXCEPTION(std::invalid_argument("resulting offset is negative"));
 99            if ((unsigned long long)offset > (size_t)~0) {
100                MORDOR_THROW_EXCEPTION(std::invalid_argument(
101                    "Memory stream position cannot exceed virtual address space."));
102            }
103            m_read.clear();
104            m_read.copyIn(m_original, m_original.readAvailable());
105            m_offset = (size_t)offset;
106            m_read.consume((std::min)((size_t)offset, size));
107            return (long long)m_offset;
108        case CURRENT:
109            if (offset < 0) {
110                return seek(m_offset + offset, BEGIN);
111            } else {
112                // Optimized forward seek
113                if (m_offset + offset > (size_t)~0) {
114                    MORDOR_THROW_EXCEPTION(std::invalid_argument(
115                        "Memory stream position cannot exceed virtual address space."));
116                }
117                if (m_offset <= size) {
118                    m_read.consume((std::min)((size_t)offset, size - m_offset));
119                    m_offset += (size_t)offset;
120                }
121                return (long long)m_offset;
122            }
123        case END:
124            // Change this into a CURRENT to try and catch an optimized forward
125            // seek
126            return seek(size + offset - m_offset, CURRENT);
127        default:
128            MORDOR_ASSERT(false);
129            return 0;
130    }
131}
132
133long long
134MemoryStream::size()
135{
136    return (long long)m_original.readAvailable();
137}
138
139void
140MemoryStream::truncate(long long size)
141{
142    MORDOR_ASSERT(size >= 0);
143    if ((unsigned long long)size > (size_t)~0) {
144        MORDOR_THROW_EXCEPTION(std::invalid_argument(
145            "Memory stream size cannot exceed virtual address space."));
146    }
147    size_t currentSize = m_original.readAvailable();
148
149    if (currentSize == (size_t)size) {
150    } else if (currentSize > (size_t)size) {
151        m_original.truncate((size_t)size);
152        if (m_offset > (size_t)size)
153            m_read.clear();
154        else
155            m_read.truncate((size_t)size - m_offset);
156    } else {
157        size_t needed = (size_t)size - currentSize;
158        m_original.reserve(needed);
159        std::vector<iovec> iovs = m_original.writeBuffers(needed);
160        for (std::vector<iovec>::iterator it(iovs.begin());
161            it != iovs.end();
162            ++it) {
163            memset(it->iov_base, 0, it->iov_len);
164        }
165        m_original.produce(needed);
166        // Reset the read buf so we're referencing the same memory
167        m_read.clear();
168        m_read.copyIn(m_original);
169        m_read.consume((std::min)(m_offset, (size_t)size));
170    }
171
172    MORDOR_ASSERT(m_original.readAvailable() == (size_t)size);
173}
174
175ptrdiff_t
176MemoryStream::find(char delim, size_t sanitySize, bool throwIfNotFound)
177{
178    ptrdiff_t result = m_read.find(delim,
179        (std::min)(sanitySize, m_read.readAvailable()));
180    if (result != -1)
181        return result;
182    if (throwIfNotFound)
183        MORDOR_THROW_EXCEPTION(UnexpectedEofException());
184    return -(ptrdiff_t)m_read.readAvailable() - 1;
185}
186
187ptrdiff_t
188MemoryStream::find(const std::string &str, size_t sanitySize, bool throwIfNotFound)
189{
190    ptrdiff_t result = m_read.find(str,
191        (std::min)(sanitySize, m_read.readAvailable()));
192    if (result != -1)
193        return result;
194    if (throwIfNotFound)
195        MORDOR_THROW_EXCEPTION(UnexpectedEofException());
196    return -(ptrdiff_t)m_read.readAvailable() - 1;
197}
198
199}