<commit_before>package com.subgraph.orchid.directory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import com.subgraph.orchid.ConsensusDocument;
import com.subgraph.orchid.ConsensusDocument.RequiredCertificate;
import com.subgraph.orchid.Directory;
import com.subgraph.orchid.DirectoryServer;
import com.subgraph.orchid.GuardEntry;
import com.subgraph.orchid.KeyCertificate;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.RouterDescriptor;
import com.subgraph.orchid.RouterStatus;
import com.subgraph.orchid.TorConfig;
import com.subgraph.orchid.TorException;
import com.subgraph.orchid.crypto.TorRandom;
import com.subgraph.orchid.data.HexDigest;
import com.subgraph.orchid.data.RandomSet;
import com.subgraph.orchid.events.Event;
import com.subgraph.orchid.events.EventHandler;
import com.subgraph.orchid.events.EventManager;

public class DirectoryImpl implements Directory {
	private final static Logger logger = Logger.getLogger(DirectoryImpl.class.getName());

	private final Object loadLock = new Object();
	private boolean isLoaded = false;
	
	private final DirectoryStoreImpl store;
	private final StateFile stateFile;
	private final Map<HexDigest, RouterImpl> routersByIdentity;
	private final Map<String, RouterImpl> routersByNickname;
	private final RandomSet<RouterImpl> directoryCaches;
	private final Set<ConsensusDocument.RequiredCertificate> requiredCertificates;
	private boolean haveMinimumRouterInfo;
	private boolean needRecalculateMinimumRouterInfo;
	private final EventManager consensusChangedManager;
	private final TorRandom random;
	private ConsensusDocument currentConsensus;
	private ConsensusDocument consensusWaitingForCertificates;
	private boolean descriptorsDirty;

	public DirectoryImpl(TorConfig config) {
		store = new DirectoryStoreImpl(config);
		stateFile = new StateFile(store, this);
		routersByIdentity = new HashMap<HexDigest, RouterImpl>();
		routersByNickname = new HashMap<String, RouterImpl>();
		directoryCaches = new RandomSet<RouterImpl>();
		requiredCertificates = new HashSet<ConsensusDocument.RequiredCertificate>();
		consensusChangedManager = new EventManager();
		random = new TorRandom();
	}

	public synchronized boolean haveMinimumRouterInfo() {
		if(needRecalculateMinimumRouterInfo) {
			checkMinimumRouterInfo();
		}
		return haveMinimumRouterInfo;
	}

	private synchronized void checkMinimumRouterInfo() {
		if(currentConsensus == null || !currentConsensus.isLive()) {
			needRecalculateMinimumRouterInfo = true;
			haveMinimumRouterInfo = false;
			return;
		}

		int routerCount = 0;
		int descriptorCount = 0;
		for(Router r: routersByIdentity.values()) {
			routerCount++;
			if(!r.isDescriptorDownloadable())
				descriptorCount++;
		}
		needRecalculateMinimumRouterInfo = false;
		haveMinimumRouterInfo = (descriptorCount * 4 > routerCount);
	}

	public void loadFromStore() {
		logger.info("Loading cached network information from disk");
		synchronized(loadLock) {
			logger.info("Loading certificates");
			store.loadCertificates(this);
			logger.info("Loading consensus");
			store.loadConsensus(this);
			logger.info("Loading descriptors");
			store.loadRouterDescriptors(this);
			logger.info("loading state file");
			store.loadStateFile(stateFile);
			isLoaded = true;
			loadLock.notifyAll();
		}
	}

	public void waitUntilLoaded() {
		synchronized (loadLock) {
			while(!isLoaded) {
				try {
					loadLock.wait();
				} catch (InterruptedException e) {<commit_msg>Prevent multiple loading of cache files and improve logging.<commit_after>package com.subgraph.orchid.directory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import com.subgraph.orchid.ConsensusDocument;
import com.subgraph.orchid.ConsensusDocument.RequiredCertificate;
import com.subgraph.orchid.Directory;
import com.subgraph.orchid.DirectoryServer;
import com.subgraph.orchid.GuardEntry;
import com.subgraph.orchid.KeyCertificate;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.RouterDescriptor;
import com.subgraph.orchid.RouterStatus;
import com.subgraph.orchid.TorConfig;
import com.subgraph.orchid.TorException;
import com.subgraph.orchid.crypto.TorRandom;
import com.subgraph.orchid.data.HexDigest;
import com.subgraph.orchid.data.RandomSet;
import com.subgraph.orchid.events.Event;
import com.subgraph.orchid.events.EventHandler;
import com.subgraph.orchid.events.EventManager;

public class DirectoryImpl implements Directory {
	private final static Logger logger = Logger.getLogger(DirectoryImpl.class.getName());

	private final Object loadLock = new Object();
	private boolean isLoaded = false;
	
	private final DirectoryStoreImpl store;
	private final StateFile stateFile;
	private final Map<HexDigest, RouterImpl> routersByIdentity;
	private final Map<String, RouterImpl> routersByNickname;
	private final RandomSet<RouterImpl> directoryCaches;
	private final Set<ConsensusDocument.RequiredCertificate> requiredCertificates;
	private boolean haveMinimumRouterInfo;
	private boolean needRecalculateMinimumRouterInfo;
	private final EventManager consensusChangedManager;
	private final TorRandom random;
	private ConsensusDocument currentConsensus;
	private ConsensusDocument consensusWaitingForCertificates;
	private boolean descriptorsDirty;

	public DirectoryImpl(TorConfig config) {
		store = new DirectoryStoreImpl(config);
		stateFile = new StateFile(store, this);
		routersByIdentity = new HashMap<HexDigest, RouterImpl>();
		routersByNickname = new HashMap<String, RouterImpl>();
		directoryCaches = new RandomSet<RouterImpl>();
		requiredCertificates = new HashSet<ConsensusDocument.RequiredCertificate>();
		consensusChangedManager = new EventManager();
		random = new TorRandom();
	}

	public synchronized boolean haveMinimumRouterInfo() {
		if(needRecalculateMinimumRouterInfo) {
			checkMinimumRouterInfo();
		}
		return haveMinimumRouterInfo;
	}

	private synchronized void checkMinimumRouterInfo() {
		if(currentConsensus == null || !currentConsensus.isLive()) {
			needRecalculateMinimumRouterInfo = true;
			haveMinimumRouterInfo = false;
			return;
		}

		int routerCount = 0;
		int descriptorCount = 0;
		for(Router r: routersByIdentity.values()) {
			routerCount++;
			if(!r.isDescriptorDownloadable())
				descriptorCount++;
		}
		needRecalculateMinimumRouterInfo = false;
		haveMinimumRouterInfo = (descriptorCount * 4 > routerCount);
	}

	public void loadFromStore() {
		logger.info("Loading cached network information from disk");
		synchronized(loadLock) {
			if(isLoaded) {
				return;
			}
	
			last = System.currentTimeMillis();
			logger.info("Loading certificates");
			store.loadCertificates(this);
			logElapsed();
			
			logger.info("Loading consensus");
			store.loadConsensus(this);
			logElapsed();
			
			logger.info("Loading descriptors");
			store.loadRouterDescriptors(this);
			logElapsed();
			
			logger.info("loading state file");
			store.loadStateFile(stateFile);
			logElapsed();
			
			isLoaded = true;
			loadLock.notifyAll();
		}
	}

	private long last = 0;
	private void logElapsed() {
		final long now = System.currentTimeMillis();
		final long elapsed =  now - last;
		last = now;
		logger.fine("Loaded in "+ elapsed + " ms.");
	}

	public void waitUntilLoaded() {
		synchronized (loadLock) {
			while(!isLoaded) {
				try {
					loadLock.wait();
				} catch (InterruptedException e) {
{'fraction_non_alphanumeric': 0.09518869147970432, 'fraction_numerical': 0.0009077940604331475, 'mean_word_length': 5.6482758620689655, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6434821', 'n_tokens_mistral': 2464, 'n_tokens_neox': 2192, 'n_words': 496}