-
Notifications
You must be signed in to change notification settings - Fork 106
/
Copy pathstreams.cpp
197 lines (157 loc) · 5.62 KB
/
streams.cpp
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright (C) 2020 Samsung Electronics Co., Ltd.
// See the LICENSE file in the project root for more information.
/// \file streams.cpp This file contains member definitions of following classes:
/// InStreamBuf, OutStreamBuf, StreamBuf, InStream, OutStream, IOStream.
#include <cstring>
#include <cassert>
#include "utils/limits.h"
#include <algorithm>
#include <thread>
#include "utils/streams.h"
namespace netcoredbg
{
// These constant define default size of the buffer, which typically can hold few lines of the text.
const size_t InStreamBuf::DefaultBufferSize = 2*LINE_MAX;
const size_t OutStreamBuf::DefaultBufferSize = 2*LINE_MAX;
const size_t StreamBuf::DefaultBufferSize = 2*LINE_MAX;
namespace
{
const size_t UngetChars = 1; // number of extra chars in buffer for ungetting
const size_t OverflowChars = 1; // number of extra chars in buffer for overflow
// minimal buffer sizes for input/output (1 char is minimal buffer size)
const size_t InputMinBuf = UngetChars + 1;
const size_t OutputMinBuf = OverflowChars + 1;
// constants for InStreamBuf
const static size_t MaxMoveSize = sizeof(void*) * 4;
}
// Arguments are following: `fh` -- file descriptor opened for reading,
// buf_size -- the size of the input buffer.
InStreamBuf::InStreamBuf(const FileHandle& fh, size_t buf_size)
: FileOwner(fh),
inbuf(std::max(buf_size, InputMinBuf))
{
setg(inbuf.data(), inbuf.data() + UngetChars, inbuf.data() + UngetChars);
assert(gptr() == egptr());
}
void InStreamBuf::setegptr(char* egptr)
{
assert(egptr <= endp() && egptr >= gptr());
setg(eback(), gptr(), egptr);
}
size_t InStreamBuf::min_read_size() const
{
return std::max(size_t(endp() - eback()) / 4, size_t(LINE_MAX));
}
void InStreamBuf::compactify()
{
assert(egptr() >= gptr());
assert(egptr() >= eback());
size_t free = endp() - egptr();
// if some relatively small portion of unread data occupies tail of the
// buffer -- move it to the beginning of the buffer to allow reading more
// data from the file.
if (free < min_read_size())
{
if (size_t(in_avail()) <= MaxMoveSize) // tail is not too big
{
memmove(eback() + UngetChars, gptr(), in_avail());
setg(eback(), eback() + UngetChars, eback() + UngetChars + in_avail());
}
}
}
// This functions fills input buffer (it should made at least one character be
// available in the buffer). Return value is traits_type::eof() in case of error,
// or the code of next available symbol.
int InStreamBuf::underflow()
{
compactify();
size_t free = endp() - egptr();
if (free < min_read_size() && in_avail() > 0)
return traits_type::to_int_type(*gptr());
using IOResult = IOSystem::IOResult;
IOResult res;
while (true)
{
res = IOSystem::read(file_handle, egptr(), free);
switch (res.status)
{
case IOResult::Error:
case IOResult::Eof:
return traits_type::eof();
default: break; // at least one byte was read
}
if (res.status == IOResult::Success)
break;
std::this_thread::yield(); // loop for non-blocking streams
}
assert(res.size > 0);
setg(eback(), gptr(), egptr() + res.size);
return traits_type::to_int_type(*gptr());
}
// Arguments are following: `fh` -- file descriptor opened for writing,
// buf_size -- the size of the output buffer.
OutStreamBuf::OutStreamBuf(const FileHandle& fh, size_t buf_size)
: FileOwner(fh),
outbuf(std::max(buf_size, OutputMinBuf))
{
buf_size = std::max(buf_size, OutputMinBuf); // TODO govnokod
setp(outbuf.data(), outbuf.data() + outbuf.size() - OverflowChars);
assert(pptr() == pbase());
assert(size_t(epptr() - pbase() + OverflowChars) == buf_size);
}
// Function writes data from a buffer to the file. Function ensures, that
// after return there is space in the buffer for at least one character.
// Function returns Traits::eof() on failure (write error).
int OutStreamBuf::overflow(int c)
{
if (!traits_type::eq_int_type(c, traits_type::eof()))
{
*pptr() = traits_type::to_char_type(c);
pbump(1); // need extra char at buffer
}
using IOResult = IOSystem::IOResult;
size_t size;
IOResult res;
while (true)
{
// try to write data
size = pptr() - pbase();
res = IOSystem::write(file_handle, pbase(), size);
if (res.status == IOResult::Error)
return traits_type::eof();
if (res.status == IOResult::Success)
break;
std::this_thread::yield(); // for non-blocking streams
}
// move unwritten part to the beginning of the buffer
// left == 0 for blocking streams
assert(res.size > 0);
size_t left = size - res.size;
memmove(pbase(), pbase() + res.size, left);
setp(pbase(), epptr());
pbump(int(left));
return traits_type::not_eof(c);
}
// Function flushes the buffer to the underlying file
int OutStreamBuf::sync()
{
while (true)
{
// try to write data
size_t size = pptr() - pbase();
if (size == 0)
break;
using IOResult = IOSystem::IOResult;
IOResult res = IOSystem::write(file_handle, pbase(), size);
if (res.status == IOResult::Error)
return -1;
if (res.status == IOResult::Success && size == res.size)
break; // all data written
setp(pbase() + res.size, epptr());
pbump(int(size - res.size));
std::this_thread::yield(); // for non-blocking streams
}
setp(outbuf.data(), epptr());
return 0;
}
} // ::netcoredbg