62#define HASH_METHOD "SHA512/256"
64#define HEADER_MAGIC MKTAG(u'\xFF', 'S', 'h', '$')
65#define HEADER_VERSION 3
71#define MAX_CORRUPT_BLOCKS 10
118typedef struct Block {
133static_assert(offsetof(
Spacemap, blocks) == 128,
"Spacemap header layout mismatch");
136#define DEF_SET_ONCE(ctype, atype) \
137 static int set_once_##atype(atomic_##atype *const ptr, const ctype value) \
140 av_assert1(value != 0); \
141 if (atomic_compare_exchange_strong_explicit( \
142 ptr, &prev, value, memory_order_release, memory_order_relaxed)) \
144 else if (prev == value) \
147 return AVERROR(EINVAL); \
200 munmap(
s->cache_data,
s->cache_size);
202 munmap(
s->spacemap,
s->map_size);
212 s->nb_hit,
s->nb_miss);
225 if (
size > INT64_MAX)
242 ret = set_once_ullong(&
s->spacemap->filesize, new_size);
245 "%"PRId64
", got: %"PRIu64
"!\n", new_size,
262 if (!
s->cache_dir || !
s->cache_dir[0]) {
264 "directory using the -cache_dir option.\n");
268 s->fd =
s->mapfd = -1;
273 options,
h->protocol_whitelist,
h->protocol_blacklist,
h);
285 char filename[2 * 16 + 1];
287 s->cache_path =
av_asprintf(
"%s/%s.cache",
s->cache_dir, filename);
288 s->map_path =
av_asprintf(
"%s/%s.spacemap",
s->cache_dir, filename);
289 if (!
s->cache_path || !
s->map_path) {
295 s->cache_path,
s->inner->filename);
298 s->mapfd =
s->fd >= 0 ?
avpriv_open(
s->map_path, O_RDWR | O_CREAT, 0660) : -1;
299 if (
s->fd < 0 ||
s->mapfd < 0) {
311 s->block_size = 1 <<
s->block_shift;
332 int64_t last_block = last_pos >>
s->block_shift;
341 "back to normal read/write\n",
av_err2str(ret));
353 h->max_packet_size =
s->block_size;
354 h->min_packet_size =
s->block_size;
370 munmap(
s->cache_data,
s->cache_size);
371 s->cache_data =
NULL;
376 int ret = fstat(
s->fd, &st);
389 s->cache_data = mmap(
NULL,
filesize, PROT_READ | PROT_WRITE, MAP_SHARED,
s->fd, 0);
390 if (
s->cache_data == MAP_FAILED) {
391 s->cache_data =
NULL;
402 int ret, did_grow = 0, locked = 0;
403 if (map_size <= s->map_size)
408 ret = fstat(
s->mapfd, &st);
414 if (st.st_size >= map_size)
418 ret = flock(
s->mapfd, LOCK_EX);
426 ret = fstat(
s->mapfd, &st);
432 if (st.st_size >= map_size)
435 ret = ftruncate(
s->mapfd, map_size);
440 st.st_size = map_size;
445 munmap(
s->spacemap,
s->map_size);
446 s->map_size = st.st_size;
447 s->spacemap = mmap(
NULL,
s->map_size, PROT_READ | PROT_WRITE, MAP_SHARED,
s->mapfd, 0);
448 if (
s->spacemap == MAP_FAILED) {
456 flock(
s->mapfd, LOCK_UN);
464 flock(
s->mapfd, LOCK_UN);
473 size_t map_bytes =
sizeof(
Spacemap) + num_blocks *
sizeof(
Block);
485 if (map_bytes < num_blocks)
488 const off_t old_size =
s->map_size;
494 if (
s->map_size > old_size) {
497 "%s %zu bytes, capacity: %"PRId64
" blocks = %"PRId64
" MB\n",
498 ret ?
"Resized spacemap to" :
"Mapped spacemap with",
499 (
size_t)
s->map_size, num_blocks,
500 (num_blocks * (
int64_t)
s->block_size) >> 20);
514 if ((ret = set_once_uint(&
s->spacemap->header_magic,
HEADER_MAGIC)) < 0 ||
526 ret = set_once_ushort(&
s->spacemap->block_shift,
s->block_shift);
530 "but requested block shift is %d.\n",
shift,
s->block_shift);
539 ret = set_once_uchar(&
s->spacemap->hash[
i],
hash[
i]);
629 const int64_t block_id =
s->pos >>
s->block_shift;
631 const int64_t block_pos = block_id *
s->block_size;
637 Block *
const block = &
s->spacemap->blocks[block_id];
640 int verify_read = 0, acquired = 0;
656 av_assert1(block_pos + block_size <= s->cache_size);
657 tmp =
s->cache_data + block_pos;
664 if (
s->retry_corrupt) {
677 "offset 0x%"PRIx64
": expected CRC: 0x%08X, got: 0x%08X\n",
678 block_id, block_pos,
state, crc);
679 if (
s->retry_corrupt) {
709 "assuming cache file is completely broken.\n");
715 if (
s->read_only ||
s->write_err)
719 memory_order_acquire,
720 memory_order_acquire))
734 }
else if (pending_since) {
736 if (
new - pending_since >=
s->timeout)
755#define RELEASE_PENDING(block, state) \
758 av_assert1(state == BLOCK_PENDING); \
759 atomic_compare_exchange_strong_explicit( \
760 &block->state, &state, BLOCK_NONE, memory_order_relaxed, \
761 memory_order_relaxed); \
768 const int read_only =
s->read_only ||
s->write_err || verify_read;
769 int64_t inner_pos = read_only ?
s->pos : block_pos;
770 if (
s->inner_pos != inner_pos) {
771 inner_pos =
ffurl_seek(
s->inner, inner_pos, SEEK_SET);
780 s->inner_pos = inner_pos;
790 s->inner_pos = inner_pos + ret;
794 if (verify_read && memcmp(buf,
tmp, ret)) {
796 "in block 0x%"PRIx64
" at offset 0x%"PRIx64
" + %"PRId64
"!\n",
797 ret, block_id, block_pos,
offset);
801 s->pos =
s->inner_pos;
806 if (
s->cache_data && acquired) {
808 tmp =
s->cache_data + block_pos;
821 while (bytes_read < block_size) {
822 ret =
ffurl_read(
s->inner, &
tmp[bytes_read], block_size - bytes_read);
838 memory_order_relaxed,
839 memory_order_relaxed);
847 if (bytes_read < block_size) {
856 if (bytes_read > 0) {
868 "offset 0x%"PRIx64
", CRC 0x%08X\n", bytes_read, block_id,
925 return s->pos =
s->inner_pos = res;
947 return ret > 0 ?
FFMAX(ret,
s->block_size) :
s->block_size;
950#define OFFSET(x) offsetof(SharedContext, x)
951#define D AV_OPT_FLAG_DECODING_PARAM
955 {
"block_shift",
"Set the base 2 logarithm of the block size",
OFFSET(block_shift),
AV_OPT_TYPE_INT, {.i64 = 15}, 9, 30, .flags =
D },
956 {
"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 },
957 {
"cache_verify",
"Verify correctness of the cache against the source",
OFFSET(verify),
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags =
D },
958 {
"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 },
959 {
"retry_errors",
"Re-request blocks even if they previously failed",
OFFSET(retry_errors),
AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags =
D },
960 {
"retry_corrupt",
"Re-request blocks that fail the CRC check",
OFFSET(retry_corrupt),
AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags =
D },
965 .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 ff_check_interrupt(AVIOInterruptCB *cb)
Check if the user has requested to interrupt a blocking function associated with cb.
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...
#define AVIO_FLAG_NONBLOCK
Use non-blocking mode.
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)
#define RELEASE_PENDING(block, state)
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 int clamp_size(URLContext *h, int size, int64_t pos, int64_t filesize)
Describe the class of an AVClass context structure.
int write_err
write error occurred
int block_shift
requested shift; updated on init if it disagrees
int64_t filesize
once known
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.