// Runs in the cache thread static void cache_execute_control(struct priv *s) { uint64_t old_pos = stream_tell(s->stream); s->control_flush = false; switch (s->control) { case STREAM_CTRL_SET_CACHE_SIZE: s->control_res = resize_cache(s, *(int64_t *)s->control_arg); break; default: s->control_res = stream_control(s->stream, s->control, s->control_arg); } bool pos_changed = old_pos != stream_tell(s->stream); bool ok = s->control_res == STREAM_OK; if (pos_changed && !ok) { MP_ERR(s, "STREAM_CTRL changed stream pos but " "returned error, this is not allowed!\n"); } else if (pos_changed || (ok && control_needs_flush(s->control))) { MP_VERBOSE(s, "Dropping cache due to control()\n"); s->read_filepos = stream_tell(s->stream); s->control_flush = true; cache_drop_contents(s); } update_cached_controls(s); s->control = CACHE_CTRL_NONE; pthread_cond_signal(&s->wakeup); }
/** * ctpl_input_stream_peek: * @stream: A #CtplInputStream * @buffer: buffer to fill with the peeked data * @count: number of bytes to peek (must be less than or qual to %G_MAXSSIZE, or * a %G_IO_ERROR_INVALID_ARGUMENT will be thrown) * @error: return location for errors, or %NULL to ignore them * * Peeks data from a #CtplInputStream. Peeking data is like reading, but it * doesn't removes the data from the stream. * * <warning> * <para> * A peek might resize the internal stream's cache to fit at least @count. * Therefore, peeking too much data at once should be done with some care. * </para> * </warning> * * Returns: the number of bytes peeked, or -1 on error * * Since: 0.2 */ gssize ctpl_input_stream_peek (CtplInputStream *stream, void *buffer, gsize count, GError **error) { gssize read_size; if (G_UNLIKELY (count > G_MAXSSIZE)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Too large count value passed to %s: %"G_GSIZE_FORMAT, G_STRFUNC, count); return -1; } if ((stream->buf_size - stream->buf_pos) < count && ! resize_cache (stream, stream->buf_pos + count, error)) { read_size = -1; } else { /* if the buffer is smaller that the request it is at EOF */ read_size = stream->buf_size - stream->buf_pos; if ((gssize)count < read_size) { read_size = (gssize)count; } memcpy (buffer, &stream->buffer[stream->buf_pos], (gsize)read_size); } return read_size; }
/** * ctpl_input_stream_peek_word: * @stream: A #CtplInputStream * @accept: string of the character acceptable for the word * @accept_len: length of @accept, can be -1 if @accept is 0-terminated * @max_len: (default -1): maximum number of bytes to peek, or -1 for no limit * @length: (out) (allow-none): return location for the length of the read word, * or %NULL * @error: return location for errors, or %NULL to ignore them * * Peeks a word from a #CtplInputStream. See ctpl_input_stream_peek() and * ctpl_input_stream_read_word(). * * Returns: A newly allocated string containing the peeked word that should be * freed with g_free() when no longer needed; or %NULL on error. * * Since: 0.2 */ gchar * ctpl_input_stream_peek_word (CtplInputStream *stream, const gchar *accept, gssize accept_len, gssize max_len, gsize *length, GError **error) { gboolean success = FALSE; GString *word; gsize accept_length; gsize max_length; accept_length = (accept_len < 0) ? strlen (accept) : (gsize)accept_len; max_length = (max_len < 0) ? G_MAXSIZE : (gsize)max_len; word = g_string_new (NULL); if (ensure_cache_filled (stream, error)) { gsize pos = stream->buf_pos; success = TRUE; do { gchar c = stream->buffer[pos++]; if (memchr (accept, c, accept_length)) { g_string_append_c (word, c); } else { break; } if (pos >= stream->buf_size) { success = resize_cache (stream, stream->buf_size + INPUT_STREAM_GROW_SIZE, error); } } while (success && pos < stream->buf_size && word->len <= max_length); } if (success && length) { *length = word->len; } return g_string_free (word, ! success); }
// return 1 on success, 0 if the cache is disabled/not needed, and -1 on error // or if the cache is disabled int stream_cache_init(stream_t *cache, stream_t *stream, struct mp_cache_opts *opts) { if (opts->size < 1) return 0; struct priv *s = talloc_zero(NULL, struct priv); s->log = cache->log; s->eof_pos = -1; cache_drop_contents(s); s->seek_limit = opts->seek_min * 1024ULL; s->back_size = opts->back_buffer * 1024ULL; int64_t cache_size = opts->size * 1024ULL; int64_t file_size = stream_get_size(stream); if (file_size >= 0) cache_size = MPMIN(cache_size, file_size); if (resize_cache(s, cache_size) != STREAM_OK) { MP_ERR(s, "Failed to allocate cache buffer.\n"); talloc_free(s); return -1; } MP_VERBOSE(cache, "Cache size set to %lld KiB (%lld KiB backbuffer)\n", (long long)(s->buffer_size / 1024), (long long)(s->back_size / 1024)); pthread_mutex_init(&s->mutex, NULL); pthread_cond_init(&s->wakeup, NULL); cache->priv = s; s->cache = cache; s->stream = stream; cache->seek = cache_seek; cache->fill_buffer = cache_fill_buffer; cache->control = cache_control; cache->close = cache_uninit; int64_t min = opts->initial * 1024ULL; if (min > s->buffer_size - FILL_LIMIT) min = s->buffer_size - FILL_LIMIT; s->seekable = stream->seekable; if (pthread_create(&s->cache_thread, NULL, cache_thread, s) != 0) { MP_ERR(s, "Starting cache thread failed.\n"); return -1; } s->cache_thread_running = true; // wait until cache is filled with at least min bytes if (min < 1) return 1; for (;;) { if (mp_cancel_test(cache->cancel)) return -1; int64_t fill; int idle; if (stream_control(s->cache, STREAM_CTRL_GET_CACHE_FILL, &fill) < 0) break; if (stream_control(s->cache, STREAM_CTRL_GET_CACHE_IDLE, &idle) < 0) break; MP_INFO(s, "\rCache fill: %5.2f%% " "(%" PRId64 " bytes) ", 100.0 * fill / s->buffer_size, fill); if (fill >= min) break; if (idle) break; // file is smaller than prefill size // Wake up if the cache is done reading some data (or on timeout/abort) pthread_mutex_lock(&s->mutex); s->control = CACHE_CTRL_PING; pthread_cond_signal(&s->wakeup); cache_wakeup_and_wait(s, &(double){0}); pthread_mutex_unlock(&s->mutex); }