stream: actually drop unneeded buffer

The buffer can be larger than the normal size when "peeking" is used
(such as done with some file formats, where a large number of bytes masy
need to be "peeked" at the beginning, because FFmpeg). Once normal
operation resumes, it's supposed to free this buffer again. Apparently
this didn't happen as intended, because normal reading did have no way
to discard back buffer before/while resizing the buffer. There's only a
path for discarding the back buffer when actually reading.

It seems like this unfortunately needs 2 code paths for discarding old
data. Just put it into stream_resize_buffer(), where it's rather
non-tricky (discarding can be done by adjusting the copy offset when
moving data to the new allocation). The function now drops old data if
it doesn't fit into the allocation. The caller must ensure that the new
size is sufficient; the function signature changes only so the size of
the implicitly guaranteed kept part can be checked with assert().
This commit is contained in:
wm4 2020-04-10 12:29:03 +02:00
parent c3f40e513b
commit 40c8df8a54
1 changed files with 21 additions and 16 deletions

View File

@ -264,24 +264,28 @@ static int ring_copy(struct stream *s, void *dst, int len, int pos)
// Does nothing if the size is adequate. Calling this with 0 ensures it uses the
// default buffer size if possible.
// The caller must check whether enough data was really allocated.
// Returns false if buffer allocation failed.
static bool stream_resize_buffer(struct stream *s, uint32_t new)
// keep: keep at least [buf_end-keep, buf_end] (used for assert()s only)
// new: new total size of buffer
// returns: false if buffer allocation failed, true if reallocated or size ok
static bool stream_resize_buffer(struct stream *s, int keep, int new)
{
// Keep all valid buffer.
int old_used_len = s->buf_end - s->buf_start;
int old_pos = s->buf_cur - s->buf_start;
new = MPMAX(new, old_used_len);
assert(keep >= s->buf_end - s->buf_cur);
assert(keep <= new);
// This much is always required.
new = MPMAX(new, s->requested_buffer_size);
new = MPMIN(new, STREAM_MAX_BUFFER_SIZE);
new = mp_round_next_power_of_2(new);
assert(keep <= new); // can't fail (if old buffer size was valid)
if (new == s->buffer_mask + 1)
return true;
MP_DBG(s, "resize stream to %d bytes\n", new);
int old_pos = s->buf_cur - s->buf_start;
int old_used_len = s->buf_end - s->buf_start;
int skip = old_used_len > new ? old_used_len - new : 0;
MP_DBG(s, "resize stream to %d bytes, drop %d bytes\n", new, skip);
void *nbuf = ta_alloc_size(s, new);
if (!nbuf)
@ -289,11 +293,12 @@ static bool stream_resize_buffer(struct stream *s, uint32_t new)
int new_len = 0;
if (s->buffer)
new_len = ring_copy(s, nbuf, new, s->buf_start);
assert(new_len == old_used_len);
assert(old_pos <= old_used_len);
new_len = ring_copy(s, nbuf, new, s->buf_start + skip);
assert(new_len == old_used_len - skip);
assert(old_pos >= skip); // "keep" too low
assert(old_pos - skip <= new_len);
s->buf_start = 0;
s->buf_cur = old_pos;
s->buf_cur = old_pos - skip;
s->buf_end = new_len;
ta_free(s->buffer);
@ -379,7 +384,7 @@ static int stream_create_instance(const stream_info_t *sinfo,
return r;
}
if (!stream_resize_buffer(s, 0)) {
if (!stream_resize_buffer(s, 0, 0)) {
free_stream(s);
return STREAM_ERROR;
}
@ -506,7 +511,7 @@ static bool stream_read_more(struct stream *s, int forward)
// Keep guaranteed seek-back.
int buf_old = MPMIN(s->buf_cur - s->buf_start, s->requested_buffer_size / 2);
if (!stream_resize_buffer(s, buf_old + forward))
if (!stream_resize_buffer(s, buf_old + forward_avail, buf_old + forward))
return false;
int buf_alloc = s->buffer_mask + 1;
@ -648,7 +653,7 @@ void stream_drop_buffers(stream_t *s)
s->pos = stream_tell(s);
s->buf_start = s->buf_cur = s->buf_end = 0;
s->eof = 0;
stream_resize_buffer(s, 0);
stream_resize_buffer(s, 0, 0);
}
// Seek function bypassing the local stream buffer.