60#define HASH_METHOD "SHA512/256"
62#define HEADER_MAGIC MKTAG(u'\xFF', 'S', 'h', '$')
63#define HEADER_VERSION 3
69#define MAX_CORRUPT_BLOCKS 10
116typedef struct Block {
132#define DEF_SET_ONCE(ctype, atype) \
133 static int set_once_##atype(atomic_##atype *const ptr, const ctype value) \
136 av_assert1(value != 0); \
137 if (atomic_compare_exchange_strong_explicit( \
138 ptr, &prev, value, memory_order_release, memory_order_relaxed)) \
140 else if (prev == value) \
143 return AVERROR(EINVAL); \
195 munmap(
s->cache_data,
s->cache_size);
197 munmap(
s->spacemap,
s->map_size);
207 s->nb_hit,
s->nb_miss);
229 ret = set_once_ullong(&
s->spacemap->filesize, new_size);
232 "%"PRId64
", got: %"PRIu64
"!\n", new_size,
249 if (!
s->cache_dir || !
s->cache_dir[0]) {
251 "directory using the -cache_dir option.\n");
255 s->fd =
s->mapfd = -1;
260 options,
h->protocol_whitelist,
h->protocol_blacklist,
h);
272 char filename[2 * 16 + 1];
274 sprintf(&filename[
i * 2],
"%02X",
hash[
i]);
275 s->cache_path =
av_asprintf(
"%s/%s.cache",
s->cache_dir, filename);
276 s->map_path =
av_asprintf(
"%s/%s.spacemap",
s->cache_dir, filename);
277 if (!
s->cache_path || !
s->map_path) {
283 s->cache_path,
s->inner->filename);
287 if (
s->fd < 0 ||
s->mapfd < 0) {
325 "back to normal read/write\n",
av_err2str(ret));
337 h->max_packet_size =
s->block_size;
338 h->min_packet_size =
s->block_size;
354 munmap(
s->cache_data,
s->cache_size);
355 s->cache_data =
NULL;
360 int ret = fstat(
s->fd, &st);
373 s->cache_data = mmap(
NULL,
filesize, PROT_READ | PROT_WRITE, MAP_SHARED,
s->fd, 0);
374 if (
s->cache_data == MAP_FAILED) {
375 s->cache_data =
NULL;
386 int ret, did_grow = 0, locked = 0;
387 if (map_size <= s->map_size)
392 ret = fstat(
s->mapfd, &st);
398 if (st.st_size >= map_size)
402 ret = flock(
s->mapfd, LOCK_EX);
410 ret = fstat(
s->mapfd, &st);
416 if (st.st_size >= map_size)
419 ret = ftruncate(
s->mapfd, map_size);
424 st.st_size = map_size;
429 munmap(
s->spacemap,
s->map_size);
430 s->map_size = st.st_size;
431 s->spacemap = mmap(
NULL,
s->map_size, PROT_READ | PROT_WRITE, MAP_SHARED,
s->mapfd, 0);
432 if (
s->spacemap == MAP_FAILED) {
440 flock(
s->mapfd, LOCK_UN);
448 flock(
s->mapfd, LOCK_UN);
457 size_t map_bytes =
sizeof(
Spacemap) + num_blocks *
sizeof(
Block);
466 if (map_bytes < num_blocks)
469 const off_t old_size =
s->map_size;
475 if (
s->map_size > old_size) {
478 "%s %zu bytes, capacity: %"PRId64
" blocks = %"PRId64
" MB\n",
479 ret ?
"Resized spacemap to" :
"Mapped spacemap with",
480 (
size_t)
s->map_size, num_blocks,
481 (num_blocks * (
int64_t)
s->block_size) >> 20);
495 if ((ret = set_once_uint(&
s->spacemap->header_magic,
HEADER_MAGIC)) < 0 ||
507 ret = set_once_ushort(&
s->spacemap->block_shift,
s->block_shift);
511 "but requested block shift is %d.\n",
shift,
s->block_shift);
519 ret = set_once_uchar(&
s->spacemap->hash[
i],
hash[
i]);
523 for (
int j = 0; j < 32; j++)
526 for (
int j = 0; j < 32; j++)
606 const int64_t block_pos = block_id *
s->block_size;
607 int block_size =
clamp_size(
h,
s->block_size, block_pos);
612 Block *
const block = &
s->spacemap->blocks[block_id];
615 int verify_read = 0, is_race = 0;
626 av_assert1(block_pos + block_size <= s->cache_size);
627 tmp =
s->cache_data + block_pos;
634 if (
s->retry_corrupt) {
647 "offset 0x%"PRIx64
": expected CRC: 0x%08X, got: 0x%08X\n",
648 block_id, block_pos,
state, crc);
649 if (
s->retry_corrupt) {
677 "assuming cache file is completely broken.\n");
687 memory_order_acquire,
688 memory_order_acquire))
702 }
else if (pending_since) {
704 if (
new - pending_since >=
s->timeout) {
721 const int read_only =
s->read_only ||
s->write_err || verify_read;
722 int64_t inner_pos = read_only ?
s->pos : block_pos;
723 if (
s->inner_pos != inner_pos) {
724 inner_pos =
ffurl_seek(
s->inner, inner_pos, SEEK_SET);
734 memory_order_relaxed,
735 memory_order_relaxed);
741 s->inner_pos = inner_pos;
751 if (verify_read && memcmp(buf,
tmp, ret)) {
753 "in block 0x%"PRIx64
" at offset 0x%"PRIx64
" + %"PRId64
"!\n",
754 ret, block_id, block_pos,
offset);
757 s->pos =
s->inner_pos = inner_pos + ret;
762 if (
s->cache_data && !is_race) {
764 tmp =
s->cache_data + block_pos;
777 while (bytes_read < block_size) {
778 ret =
ffurl_read(
s->inner, &
tmp[bytes_read], block_size - bytes_read);
793 memory_order_relaxed,
794 memory_order_relaxed);
802 if (bytes_read < block_size) {
809 if (bytes_read > 0) {
819 memory_order_relaxed,
820 memory_order_relaxed);
824 "offset 0x%"PRIx64
", CRC 0x%08X\n", bytes_read, block_id,
875 return s->pos =
s->inner_pos = res;
897 return ret > 0 ?
FFMAX(ret,
s->block_size) :
s->block_size;
900#define OFFSET(x) offsetof(SharedContext, x)
901#define D AV_OPT_FLAG_DECODING_PARAM
905 {
"block_shift",
"Set the base 2 logarithm of the block size",
OFFSET(block_shift),
AV_OPT_TYPE_INT, {.i64 = 15}, 9, 30, .flags =
D },
906 {
"read_only",
"Don't write data to the cache, only read from it",
OFFSET(read_only),
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags =
D },
907 {
"cache_verify",
"Verify correctness of the cache against the source",
OFFSET(verify),
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags =
D },
908 {
"cache_timeout",
"Time in us to wait before re-fetching pending blocks",
OFFSET(timeout),
AV_OPT_TYPE_INT64, {.i64 = 10000}, 0, INT64_MAX, .flags =
D },
909 {
"retry_errors",
"Re-request blocks even if they previously failed",
OFFSET(retry_errors),
AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags =
D },
910 {
"retry_corrupt",
"Re-request blocks that fail the CRC check",
OFFSET(retry_corrupt),
AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags =
D },
915 .class_name =
"shared",
static int read_block(ALSDecContext *ctx, ALSBlockData *bd)
Read the block data.
static uint8_t hash[HASH_SIZE]
static AVFormatContext * ctx
static av_cold void close(AVCodecParserContext *s)
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
#define av_assert0(cond)
assert() equivalent, that is always enabled.
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist, URLContext *parent)
Create an URLContext for accessing to the resource indicated by url, and open it.
int64_t ffurl_size(URLContext *h)
Return the filesize of the resource accessed by h, AVERROR(ENOSYS) if the operation is not supported ...
int ffurl_close(URLContext *h)
int ffurl_get_short_seek(void *urlcontext)
Return the current short seek threshold value for this URL.
int ffurl_get_file_handle(URLContext *h)
Return the file descriptor associated with this URL.
#define AVSEEK_SIZE
Passing this as the "whence" parameter to a seek function causes it to return the filesize without se...
char * av_asprintf(const char *fmt,...)
#define flags(name, subs,...)
#define i(width, name, range_min, range_max)
Public header for CRC hash function implementation.
#define atomic_compare_exchange_strong_explicit(object, expected, desired, success, failure)
#define atomic_load_explicit(object, order)
#define atomic_load(object)
#define atomic_store_explicit(object, desired, order)
static struct @346255127015250356166251341105367306144006377143 state
static int64_t filesize(AVIOContext *pb)
@ AV_OPT_TYPE_INT64
Underlying C type is int64_t.
@ AV_OPT_TYPE_INT
Underlying C type is int.
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
const AVCRC * av_crc_get_table(AVCRCId crc_id)
Get an initialized standard CRC table.
uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length)
Calculate the CRC of a block.
#define AVERROR_EXIT
Immediate exit was requested; the called function should not be restarted.
#define AVERROR_EOF
End of file.
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
void av_hash_freep(AVHashContext **ctx)
Free hash context and set hash context pointer to NULL.
void av_hash_init(AVHashContext *ctx)
Initialize or reset a hash context.
void av_hash_update(AVHashContext *ctx, const uint8_t *src, size_t len)
Update a hash context with additional data.
int av_hash_alloc(AVHashContext **ctx, const char *name)
Allocate a hash context for the algorithm specified by name.
void av_hash_final(AVHashContext *ctx, uint8_t *dst)
Finalize a hash context and compute the actual hash value.
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
#define AV_LOG_WARNING
Something somehow does not look correct.
#define AV_LOG_VERBOSE
Detailed information.
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
const char * av_default_item_name(void *ptr)
Return the context name.
int av_strstart(const char *str, const char *pfx, const char **ptr)
Return non-zero if pfx is a prefix of str.
#define LIBAVUTIL_VERSION_INT
int av_hash_get_size(const AVHashContext *ctx)
static int shift(int a, int b)
Macro definitions for various function/variable attributes.
int avpriv_open(const char *filename, int flags,...)
A wrapper for open() setting O_CLOEXEC.
Memory handling functions.
const URLProtocol ff_shared_protocol
static int shared_read(URLContext *h, unsigned char *buf, int size)
static int read_cache(SharedContext *s, uint8_t *buf, size_t size, off_t offset)
static int spacemap_grow(URLContext *h, int64_t block)
#define DEF_SET_ONCE(ctype, atype)
static int set_filesize(URLContext *h, int64_t new_size)
static int shared_get_short_seek(URLContext *h)
#define HASH_METHOD
This hash should be resistant against collision attacks, so that an attacker could not generate e....
static int shared_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
static int64_t shared_seek(URLContext *h, int64_t pos, int whence)
static int shared_close(URLContext *h)
static int shared_get_file_handle(URLContext *h)
static int64_t get_filesize(URLContext *h)
static int hash_uri(uint8_t hash[HASH_SIZE], const char *uri)
static int cache_map(URLContext *h, int64_t filesize)
static int write_cache(SharedContext *s, const uint8_t *buf, size_t size, off_t offset)
static const AVClass shared_context_class
static int spacemap_remap(URLContext *h, size_t map_size)
#define MAX_CORRUPT_BLOCKS
Hard watershed of consecutive failed blocks before we give up on the cache file altogether and assume...
static uint32_t get_block_crc(const uint8_t *block, size_t block_size)
static int spacemap_init(URLContext *h, const uint8_t hash[HASH_SIZE])
@ BLOCK_NONE
block is not cached
@ BLOCK_PENDING
a thread is currently trying to write this block
@ BLOCK_FAILED
the underlying I/O source failed to read this block
static size_t clamp_size(URLContext *h, size_t size, int64_t pos)
#define FF_ARRAY_ELEMS(a)
Describe the class of an AVClass context structure.
int write_err
write error occurred
int block_shift
requested shift; may disagree with actual
uint8_t * cache_data
optional mmap of the cache file
int64_t pos
current logical position
off_t cache_size
size of mapped memory region (for munmap)
atomic_uchar hash[HASH_SIZE]
atomic_ushort block_shift
int av_usleep(unsigned usec)
Sleep for a period of time.
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
unbuffered private I/O API
static int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
Change the position that will be used by the next read/write operation on the resource accessed by h.
static int ffurl_read(URLContext *h, uint8_t *buf, int size)
Read up to size bytes from the resource accessed by h, and store the read bytes in buf.