summaryrefslogtreecommitdiff
path: root/stream-servers/ReadBuffer.cpp
blob: 5e0ef71632d7f87247d6d73bcbe0c483321bd52b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ReadBuffer.h"

#include "host-common/logging.h"

#include <algorithm>

#include <assert.h>
#include <string.h>
#include <limits.h>

namespace emugl {

ReadBuffer::ReadBuffer(size_t bufsize) {
    m_size = bufsize;
    m_buf = (unsigned char*)malloc(m_size);
    m_validData = 0;
    m_readPtr = m_buf;
}

ReadBuffer::~ReadBuffer() {
    free(m_buf);
}

void ReadBuffer::setNeededFreeTailSize(size_t size) {
    m_neededFreeTailSize = size;
}

int ReadBuffer::getData(IOStream* stream, size_t minSize) {
    assert(stream);
    assert(minSize > m_validData);

    const size_t minSizeToRead = minSize - m_validData;
    const size_t neededFreeTailThisTime =
        std::max(minSizeToRead,
                 m_neededFreeTailSize);

    size_t maxSizeToRead;
    const size_t freeTailSize = m_buf + m_size - (m_readPtr + m_validData);
    if (freeTailSize >= neededFreeTailThisTime) {
        maxSizeToRead = freeTailSize;
    } else {
        if (freeTailSize + (m_readPtr - m_buf) >= neededFreeTailThisTime) {
            // There's some gap in the beginning, if we move the data over it
            // that's going to be enough.
            memmove(m_buf, m_readPtr, m_validData);
        } else {
            // Not enough space even with moving, reallocate.
            // Note: make sure we can fit at least two of the requested packets
            //  into the new buffer to minimize the reallocations and
            //  memmove()-ing stuff around.
          size_t new_size = std::max(2 * minSizeToRead + m_validData, 2 * m_size);
          if (new_size < m_size) {  // overflow check
                new_size = INT_MAX;
            }

            const auto new_buf = (unsigned char*)malloc(new_size);
            if (!new_buf) {
                ERR("Failed to alloc %zu bytes for ReadBuffer\n", new_size);
                return -1;
            }

            memcpy(new_buf, m_readPtr, m_validData);
            free(m_buf);
            m_buf = new_buf;
            m_size = new_size;
        }
        // We can read more now, let's request it in case all data is ready
        // for reading.
        maxSizeToRead = m_size - m_validData;
        m_readPtr = m_buf;
    }

    // get fresh data into the buffer;
    int readTotal = 0;
    do {
        const size_t readNow = stream->read(m_readPtr + m_validData,
                                            maxSizeToRead - readTotal);

        if (!readNow) {
            if (readTotal > 0) {
                return readTotal;
            } else {
                return -1;
            }
        }
        readTotal += readNow;
        m_validData += readNow;
    } while (readTotal < minSizeToRead);

    return readTotal;
}

void ReadBuffer::consume(size_t amount) {
    assert(amount <= m_validData);
    m_validData -= amount;
    m_readPtr += amount;
}

void ReadBuffer::onSave(android::base::Stream* stream) {
    stream->putBe32(m_size);
    stream->putBe32(m_validData);
    stream->write(m_readPtr, m_validData);
}

void ReadBuffer::onLoad(android::base::Stream* stream) {
    const auto size = stream->getBe32();
    if (size > m_size) {
        m_size = size;
        free(m_buf);
        m_buf = (unsigned char*)malloc(m_size);
    }
    m_readPtr = m_buf;
    m_validData = stream->getBe32();
    assert(m_validData <= m_size);
    stream->read(m_readPtr, m_validData);
}

void ReadBuffer::printStats() {
    printf("ReadBuffer::%s: tail move time %f ms\n", __func__,
            (float)m_tailMoveTimeUs / 1000.0f);
    m_tailMoveTimeUs = 0;
}
}  // namespace emugl