piececache.c

来自「elinks下lynx是最重要的二个文本浏览器, 在linux下非常实用, el」· C语言 代码 · 共 1,360 行 · 第 1/3 页

C
1,360
字号
	/* The blocks bitfield is always allocated when downloading a piece. */	if (!entry->blocks)		return BITTORRENT_STATE_OK;	/* Only accept data from pending requests. */	request = get_bittorrent_peer_request(&peer->local, piece, offset, datalen);	if (!request)		return BITTORRENT_STATE_OK;	piece_length = get_bittorrent_piece_length(meta, piece);	if (!entry->data) {		entry->data = mem_mmap_alloc(piece_length);		if (!entry->data) {			add_request_to_bittorrent_piece_cache(peer->bittorrent, request);			return BITTORRENT_STATE_OK;		}	}	memcpy(&entry->data[offset], data, datalen);	set_bitfield_bit(entry->blocks, request->block);	assertm(get_bitfield_set_count(entry->blocks),		"Bit %u in bitfield size %u",		request->block, entry->blocks->bitsize);	update_bittorrent_connection_stats(bittorrent, request->length, 0, 0);	/* Remove it from the request queue so it won't be found when canceling	 * cloned requests. */	del_from_list(request);	if (request->cloned)		cancel_cloned_bittorrent_peer_requests(bittorrent, request);	mem_free(request);	if (!bitfield_is_set(entry->blocks))		return BITTORRENT_STATE_OK;	mem_free_set(&entry->blocks, NULL);	if (!bittorrent_piece_is_valid(meta, piece, entry->data, piece_length)) {		/* Bad hash ... try againing! */		cache->rejected_pieces++;		mem_mmap_free(entry->data, piece_length);		entry->data = NULL;		update_bittorrent_connection_stats(bittorrent, (off_t) -piece_length, 0, 0);		set_bittorrent_piece_cache_remaining(cache, piece, 1);		if (bittorrent->mode == BITTORRENT_MODE_END_GAME)			bittorrent->mode = BITTORRENT_MODE_NORMAL;		return BITTORRENT_STATE_OK;	}	set_bittorrent_piece_cache_completed(cache, piece);	set_bittorrent_peer_have(peer, piece);	/* Write to disk. */	errno = 0;	state = bittorrent_file_piece_translation(&bittorrent->meta, cache,						  entry, piece, BITTORRENT_WRITE);	if (state != BITTORRENT_STATE_OK) {		if (errno)			*write_errno = errno;		return state;	}	/* Handle mode changes ... */	handle_bittorrent_mode_changes(bittorrent);	return BITTORRENT_STATE_OK;}unsigned char *get_bittorrent_piece_cache_data(struct bittorrent_connection *bittorrent,				uint32_t piece){	struct bittorrent_piece_cache *cache = bittorrent->cache;	struct bittorrent_piece_cache_entry *entry;	enum bittorrent_state state;	assert(piece < bittorrent->meta.pieces);	entry = &cache->entries[piece];	if (!entry->completed)		return NULL;	if (entry->data) {		move_to_top_of_list(cache->queue, entry);		return entry->data;	}	/* Load the piece data from disk. */	state = bittorrent_file_piece_translation(&bittorrent->meta, cache,						  entry, piece, BITTORRENT_READ);	if (state != BITTORRENT_STATE_OK)		return NULL;	return entry->data;}/* ************************************************************************** *//* Asynchronous and threaded piece cache resuming: *//* ************************************************************************** */static voiddone_bittorrent_resume(struct bittorrent_piece_cache *cache){	if (cache->resume_fd == -1)		return;	clear_handlers(cache->resume_fd);	close(cache->resume_fd);	cache->resume_fd = -1;}/* Mark all pieces which desn't need to be downloaded and lock entries that span * both selected and unselected files in memory. */static voidprepare_partial_bittorrent_download(struct bittorrent_connection *bittorrent){	struct bittorrent_piece_cache *cache = bittorrent->cache;	off_t est_length = 0;	off_t completed = 0;	uint32_t piece = 0;	for (piece = 0; piece < bittorrent->meta.pieces; piece++) {		struct bittorrent_piece_cache_entry *entry;		uint32_t piece_length;		enum bittorrent_state state;		entry = &cache->entries[piece];		state = bittorrent_file_piece_translation(&bittorrent->meta,							  cache, entry, piece,							  BITTORRENT_SEEK);		assert(state == BITTORRENT_STATE_OK);		assertm(!entry->locked || entry->selected,			"All locked pieces should be selected");		if (entry->locked || !entry->selected)			cache->partial = 1;		if (entry->locked)			cache->locked_pieces++;		piece_length = get_bittorrent_piece_length(&bittorrent->meta, piece);		if (entry->selected) {			cache->partial_pieces++;			est_length += piece_length;			if (entry->completed)				completed += piece_length;		} else if (entry->remaining) {			/* Update the number of remaining pieces. */			set_bittorrent_piece_cache_remaining(cache, piece, -1);		}	}	if (cache->partial) {		bittorrent->conn->est_length = est_length;		bittorrent->conn->from = completed;	}}static voidend_bittorrent_resume(struct bittorrent_connection *bittorrent){	struct bittorrent_piece_cache *cache = bittorrent->cache;	done_bittorrent_resume(bittorrent->cache);	prepare_partial_bittorrent_download(bittorrent);	handle_bittorrent_mode_changes(bittorrent);	/* The resuming will decrement the loading count to -pieces. */	cache->loading_pieces = 0;	cache->unavailable_pieces = bittorrent->meta.pieces - cache->completed_pieces;	bittorrent_resume_callback(bittorrent);}static voidbittorrent_resume_writer(void *data, int fd){	struct bittorrent_piece_cache cache;	struct bittorrent_meta meta;	struct string metafile;	uint32_t piece;	memcpy(&metafile.length, data, sizeof(metafile.length));	metafile.source = (unsigned char *) data + 4;	if (parse_bittorrent_metafile(&meta, &metafile) != BITTORRENT_STATE_OK) {		done_bittorrent_meta(&meta);		return;	}	memset(&cache, 0, sizeof(cache));	init_list(cache.queue);	if (set_blocking_fd(fd) < 0) {		done_bittorrent_meta(&meta);		return;	}	for (piece = 0; piece < meta.pieces; piece++){		struct bittorrent_piece_cache_entry entry;		enum bittorrent_state state;		uint32_t length;		char complete;		int written;		length = get_bittorrent_piece_length(&meta, piece);		memset(&entry, 0, sizeof(entry));		/* Attempt to read this piece from cache. This is used to resume		 * a download */		state = bittorrent_file_piece_translation(&meta, &cache, &entry,							  piece, BITTORRENT_READ);		if (state == BITTORRENT_STATE_OK		    && bittorrent_piece_is_valid(&meta, piece, entry.data, length)) {			complete = 1;		} else {			complete = 0;		}		if (entry.data) {			if (state == BITTORRENT_STATE_OK)				del_from_list(&entry);			mem_mmap_free(entry.data, length);		}		written = safe_write(fd, &complete, 1);		if (written < 0)			break;	}	done_bittorrent_meta(&meta);}static voidbittorrent_resume_reader(struct bittorrent_connection *bittorrent){	struct bittorrent_piece_cache *cache = bittorrent->cache;	char completed[MAX_STR_LEN];	ssize_t size, pos;	set_connection_timeout(bittorrent->conn);	size = safe_read(cache->resume_fd, completed, sizeof(completed));	if (size <= 0) {		end_bittorrent_resume(bittorrent);		return;	}	for (pos = 0; pos < size; pos++) {		uint32_t piece = cache->resume_pos++;		uint32_t length;		if (piece >= bittorrent->meta.pieces) {			end_bittorrent_resume(bittorrent);			return;		}		assert(piece < bittorrent->meta.pieces);		if (!completed[pos])			continue;		length = get_bittorrent_piece_length(&bittorrent->meta, piece);		set_bittorrent_piece_cache_completed(cache, piece);		cache->remaining_pieces--;		bittorrent->left -= length;		bittorrent->conn->from += length;	}}static voidstart_bittorrent_resume(struct bittorrent_connection *bittorrent,			struct string *meta){	struct bittorrent_piece_cache *cache = bittorrent->cache;	struct string info;	assert(cache && cache->resume_fd == -1);	if (!init_string(&info)) return;	add_bytes_to_string(&info, (void *) &meta->length, sizeof(meta->length));	add_bytes_to_string(&info, meta->source, meta->length);	cache->resume_fd = start_thread(bittorrent_resume_writer, info.source,					info.length);	done_string(&info);	if (cache->resume_fd == -1)		return;	if (set_blocking_fd(cache->resume_fd) < 0) {		close(cache->resume_fd);		cache->resume_fd = -1;		return;	}	set_handlers(cache->resume_fd,		     (select_handler_T) bittorrent_resume_reader, NULL,		     (select_handler_T) end_bittorrent_resume, bittorrent);}/* ************************************************************************** *//* Piece cache life-cycle: *//* ************************************************************************** *//* Periodically called to shrink the cache. *//* The strategy is to  keep the n most recently accessed pieces in memory, chunk * the rest n depends on amount of memory available */voidupdate_bittorrent_piece_cache_state(struct bittorrent_connection *bittorrent){	struct bittorrent_piece_cache *cache = bittorrent->cache;	struct bittorrent_piece_cache_entry *entry, *next;	off_t cache_size = get_opt_int("protocol.bittorrent.piece_cache_size");	off_t current_size = 0;	if (!cache_size) return;	foreachsafe (entry, next, cache->queue) {		uint32_t piece_length, piece;		assertm(entry->data && entry->completed);		piece = entry - cache->entries;		assert(piece < bittorrent->meta.pieces);		assert(entry == &cache->entries[piece]);		piece_length = get_bittorrent_piece_length(&bittorrent->meta, piece);		current_size += piece_length;		/* Allow at least one piece! */		if (current_size < cache_size || entry->locked)			continue;		del_from_list(entry);		mem_mmap_free(entry->data, piece_length);		entry->data = NULL;	}}enum bittorrent_stateinit_bittorrent_piece_cache(struct bittorrent_connection *bittorrent,			    struct string *metafile){	struct bittorrent_piece_cache *cache;	uint32_t pieces = bittorrent->meta.pieces;	size_t cache_entry_size = sizeof(*cache->entries) * pieces;	uint32_t piece;	cache = mem_calloc(1, sizeof(*cache) + cache_entry_size);	if (!cache) return BITTORRENT_STATE_OUT_OF_MEM;	cache->bitfield  = init_bitfield(pieces);	cache->resume_fd = -1;	if (!cache->bitfield) {		mem_free(cache);		return BITTORRENT_STATE_OUT_OF_MEM;	}	init_list(cache->queue);	init_list(cache->free_list);	bittorrent->cache = cache;	/* Do the initialization of the connection stats; bytes left and	 * estimated length in particular. Placed here so that downloaded	 * resuming can mangle bytes left if it has to. */	update_bittorrent_connection_stats(bittorrent, 0, 0, 0); 	for (piece = 0; piece < pieces; piece++) 		set_bittorrent_piece_cache_remaining(cache, piece, 1); 	start_bittorrent_resume(bittorrent, metafile);	assert(list_empty(cache->queue)); 	return cache->resume_fd == -1 		? BITTORRENT_STATE_OK : BITTORRENT_STATE_CACHE_RESUME;}static voiddelete_bittorrent_files(struct bittorrent_connection *bittorrent){	struct bittorrent_meta *meta = &bittorrent->meta;	struct bittorrent_file *file;	foreach (file, meta->files) {		unsigned char *name;		if (!file->selected)			continue;		name = get_bittorrent_file_name(meta, file);		if (!name) continue;		unlink(name);		if (meta->type == BITTORRENT_MULTI_FILE)			remove_bittorrent_path(meta, name);		mem_free(name);	}}voiddone_bittorrent_piece_cache(struct bittorrent_connection *bittorrent){	struct bittorrent_piece_cache *cache = bittorrent->cache;	uint32_t piece;	done_bittorrent_resume(cache);	for (piece = 0; piece < bittorrent->meta.pieces; piece++) {		struct bittorrent_piece_cache_entry *entry;		size_t length;		entry = &cache->entries[piece];		assertm(entry->rarity == 0, "Rarity out of sync (%u for %u)",			entry->rarity, piece);		mem_free_if(entry->blocks);		if (!entry->data)			continue;		length = get_bittorrent_piece_length(&bittorrent->meta, piece);		mem_mmap_free(entry->data, length);	}	if (cache->delete_files)		delete_bittorrent_files(bittorrent);	free_list(cache->free_list);	mem_free_if(cache->bitfield);	mem_free(cache);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?