diff --git a/core/src/com/biglybt/core/disk/impl/DiskManagerUtil.java b/core/src/com/biglybt/core/disk/impl/DiskManagerUtil.java index 7802a63b20c..b2baac70cb9 100644 --- a/core/src/com/biglybt/core/disk/impl/DiskManagerUtil.java +++ b/core/src/com/biglybt/core/disk/impl/DiskManagerUtil.java @@ -397,8 +397,6 @@ state.setFileLink( file_info.getIndex(), from_file, to_link ); - state.save(false); - return( null ); } diff --git a/core/src/com/biglybt/core/diskmanager/file/impl/FMFileImpl.java b/core/src/com/biglybt/core/diskmanager/file/impl/FMFileImpl.java index 7b76ba083a0..ed15e8a538a 100644 --- a/core/src/com/biglybt/core/diskmanager/file/impl/FMFileImpl.java +++ b/core/src/com/biglybt/core/diskmanager/file/impl/FMFileImpl.java @@ -330,7 +330,7 @@ public void parameterChanged(String parameterName) { @Override public void moveFile( - File new_unlinked_file, + File new_linked_file, FileUtil.ProgressListener pl ) throws FMFileManagerException @@ -343,11 +343,11 @@ public void parameterChanged(String parameterName) { length_cache = getLength(); - TOTorrentFile tf = owner.getTorrentFile(); - String new_canonical_path; - - File new_linked_file = manager.getFileLink( tf.getTorrent(), tf.getIndex(), new StringInterner.FileKey( new_unlinked_file )).getFile(); + + // 3701 - switched the passed file to always be the actual destination regardless of any links, these are set up separately as required by callers + // TOTorrentFile tf = owner.getTorrentFile(); + // File new_linked_file = manager.getFileLink( tf.getTorrent(), tf.getIndex(), new StringInterner.FileKey( new_unlinked_file )).getFile(); try{ diff --git a/core/src/com/biglybt/core/diskmanager/file/impl/FMFileManagerImpl.java b/core/src/com/biglybt/core/diskmanager/file/impl/FMFileManagerImpl.java index fda91193ea2..f329cac9919 100644 --- a/core/src/com/biglybt/core/diskmanager/file/impl/FMFileManagerImpl.java +++ b/core/src/com/biglybt/core/diskmanager/file/impl/FMFileManagerImpl.java @@ -25,7 +25,6 @@ * */ -import java.io.File; import java.util.*; import com.biglybt.core.config.COConfigurationManager; @@ -65,20 +64,20 @@ } } - protected final LinkedHashMap map; + protected final LinkedHashMap map; protected final AEMonitor map_mon = new AEMonitor( "FMFileManager:Map"); - protected final HashMap links = new HashMap<>(); + protected final HashMap links = new HashMap<>(); protected final AEMonitor links_mon = new AEMonitor( "FMFileManager:Links"); protected final boolean limited; protected final int limit_size; - protected AESemaphore close_queue_sem; - protected List close_queue; - protected final AEMonitor close_queue_mon = new AEMonitor( "FMFileManager:CQ"); + protected AESemaphore close_queue_sem; + protected List close_queue; + protected final AEMonitor close_queue_mon = new AEMonitor( "FMFileManager:CQ"); - protected List files; + protected List files; protected final AEMonitor files_mon = new AEMonitor( "FMFileManager:File"); protected @@ -92,30 +91,26 @@ System.out.println( "FMFileManager::init: limit = " + limit_size ); - files = new ArrayList(); + files = new ArrayList<>(); } - map = new LinkedHashMap( limit_size, (float)0.75, true ); // ACCESS order selected - this means oldest + map = new LinkedHashMap<>( limit_size, (float)0.75, true ); // ACCESS order selected - this means oldest if ( limited ){ close_queue_sem = new AESemaphore("FMFileManager::closeqsem"); - close_queue = new LinkedList(); + close_queue = new LinkedList<>(); - Thread t = new AEThread("FMFileManager::closeQueueDispatcher") + new AEThread2("FMFileManager::closeQueueDispatcher") { @Override public void - runSupport() + run() { closeQueueDispatch(); } - }; - - t.setDaemon(true); - - t.start(); + }.start(); } } @@ -123,17 +118,16 @@ getLinksEntry( TOTorrent torrent ) { - Object links_key; + HashWrapper links_key; try{ - links_key = torrent.getHashWrapper(); }catch( Throwable e ){ Debug.printStackTrace(e); - links_key = ""; + links_key = new HashWrapper( new byte[0]); } LinkFileMap links_entry = links.get( links_key ); @@ -157,35 +151,14 @@ try{ links_mon.enter(); - LinkFileMap links_entry = getLinksEntry( torrent ); - - Iterator it = new_links.entryIterator(); - - while( it.hasNext()){ - - LinkFileMap.Entry entry = it.next(); - - int index = entry.getIndex(); - - StringInterner.FileKey source = entry.getFromFile(); - StringInterner.FileKey target = entry.getToFile(); - - // System.out.println( "setLink:" + source + " -> " + target ); - - if ( target != null && !FileUtil.areFilePathsIdentical( source.getFile(), target.getFile() )){ - - if ( index >= 0 ){ - - links_entry.put( index, source, target ); - - }else{ + try{ + HashWrapper links_key = torrent.getHashWrapper(); - links_entry.putMigration( source, target ); - } - }else{ + links.put( links_key, new_links ); + + }catch( Throwable e ){ - links_entry.remove( index, source ); - } + Debug.printStackTrace(e); } }finally{ @@ -200,23 +173,25 @@ int file_index, StringInterner.FileKey file ) { - // this function works on the currently defined links and will only accept - // them as valid if their 'from' location matches the 'file' being queried. - // if not the original file is returned, NOT null + // Reworked as of 3701 to NOT rely on the crap below... + + // x this function works on the currently defined links and will only accept + // x them as valid if their 'from' location matches the 'file' being queried. + // x if not the original file is returned, NOT null - // These semantics are important during file-move operations as the move-file - // logic does not update links until AFTER the move is complete. If we don't - // verify the 'from' path in this case then the old existing linkage overrides - // the new destination during the move process and causes it to fail with - // 'file already exists'. There is possibly an argument that we shouldn't - // take links into account when moving + // x These semantics are important during file-move operations as the move-file + // x logic does not update links until AFTER the move is complete. If we don't + // x verify the 'from' path in this case then the old existing linkage overrides + // x the new destination during the move process and causes it to fail with + // x 'file already exists'. There is possibly an argument that we shouldn't + // x take links into account when moving try{ links_mon.enter(); LinkFileMap links_entry = getLinksEntry( torrent ); - LinkFileMap.Entry entry = links_entry.getEntry( file_index, file.getFile()); + LinkFileMap.Entry entry = links_entry.getEntry( file_index ); StringInterner.FileKey res = null; @@ -226,14 +201,22 @@ }else{ + res = entry.getToFile(); + + /* if ( file.equals( entry.getFromFile())){ res = entry.getToFile(); + if ( res == null ){ + + res = file; + } }else{ res = file; } + */ } // System.out.println( "getLink:" + file + " -> " + res ); @@ -247,8 +230,11 @@ } @Override - public boolean hasLinks(TOTorrent torrent) { - return getLinksEntry(torrent).hasLinks(); + public boolean + hasLinks( + TOTorrent torrent) + { + return( getLinksEntry(torrent).size() > 0 ); } @Override @@ -565,13 +551,9 @@ public boolean hasLinks(TOTorrent torrent) { int index = entry.getIndex(); - StringInterner.FileKey source = entry.getFromFile(); StringInterner.FileKey target = entry.getToFile(); - - if ( target != null && !FileUtil.areFilePathsIdentical( source.getFile(), target.getFile())){ - - writer.println( index + ": " + source + " -> " + target ); - } + + writer.println( index + ": -> " + target ); } }finally{ diff --git a/core/src/com/biglybt/core/download/DownloadManagerState.java b/core/src/com/biglybt/core/download/DownloadManagerState.java index 1b1829f26fc..0bb68ffc03a 100644 --- a/core/src/com/biglybt/core/download/DownloadManagerState.java +++ b/core/src/com/biglybt/core/download/DownloadManagerState.java @@ -48,7 +48,7 @@ public static final String AT_PEER_SOURCES = "peersources"; public static final String AT_PEER_SOURCES_DENIED = "peersourcesdenied"; public static final String AT_TRACKER_CLIENT_EXTENSIONS = "trackerclientextensions"; - public static final String AT_FILE_LINKS_DEPRECATED = "filelinks"; + //public static final String AT_FILE_LINKS_DEPRECATED = "filelinks"; public static final String AT_FILE_LINKS2 = "filelinks2"; public static final String AT_FILE_ALLOC_REQUEST = "allocreq"; // Map public static final String AT_FILE_STORE_TYPES = "storetypes"; @@ -379,7 +379,7 @@ public void setFileLinks( List source_indexes, - List link_sources, + List link_sources_may_have_nulls, List link_destinations ); public void diff --git a/core/src/com/biglybt/core/download/impl/DownloadManagerImpl.java b/core/src/com/biglybt/core/download/impl/DownloadManagerImpl.java index 155d715f8d8..ea014b7d8fc 100644 --- a/core/src/com/biglybt/core/download/impl/DownloadManagerImpl.java +++ b/core/src/com/biglybt/core/download/impl/DownloadManagerImpl.java @@ -1994,7 +1994,8 @@ public void perform(TimerEvent event){ private void updateFileLinks( - File old_save_path, File new_save_path) + File old_save_path, + File new_save_path) { old_save_path = FileUtil.getCanonicalFileSafe( old_save_path ); @@ -2006,10 +2007,12 @@ public void perform(TimerEvent event){ Iterator it = links.entryIterator(); - List from_indexes = new ArrayList<>(); - List from_links = new ArrayList<>(); - List to_links = new ArrayList<>(); + List from_indexes = new ArrayList<>(); + List from_links_may_have_nulls = new ArrayList<>(); + List to_links = new ArrayList<>(); + DiskManagerFileInfo[] dm_files = getDiskManagerFileInfoSet().getFiles(); + while(it.hasNext()){ LinkFileMap.Entry entry = it.next(); @@ -2029,12 +2032,26 @@ public void perform(TimerEvent event){ int file_index = entry.getIndex(); - StringInterner.FileKey from_fk = entry.getFromFile(); + // preparation for future removal of the "from-file" + + File from_maybe_null; + + StringInterner.FileKey from_fk_maybe_null = entry.getFromFileMaybeNull(); - File from = FileUtil.getCanonicalFileSafe( from_fk.getFile()); + if ( from_fk_maybe_null == null ){ + + from_maybe_null = null; + + }else{ + + from_maybe_null = FileUtil.getCanonicalFileSafe( from_fk_maybe_null.getFile()); + } - updateFileLink( file_index, old_save_path, new_save_path, from, to, - from_indexes, from_links, to_links ); + updateFileLink( + file_index, + old_save_path, new_save_path, + from_maybe_null, to, + from_indexes, from_links_may_have_nulls, to_links ); }catch( Exception e ){ @@ -2042,9 +2059,9 @@ public void perform(TimerEvent event){ } } - if ( from_links.size() > 0 ){ + if ( from_links_may_have_nulls.size() > 0 ){ - download_manager_state.setFileLinks( from_indexes, from_links, to_links ); + download_manager_state.setFileLinks( from_indexes, from_links_may_have_nulls, to_links ); } } @@ -2061,7 +2078,7 @@ public void perform(TimerEvent event){ int file_index, File old_path, File new_path, - File from_loc, + File from_loc_maybe_null, File to_loc, List from_indexes, List from_links, @@ -2074,9 +2091,9 @@ public void perform(TimerEvent event){ if ( torrent.isSimpleTorrent()){ - if (!FileUtil.areFilePathsIdentical(old_path, from_loc)) { + if (from_loc_maybe_null != null && !FileUtil.areFilePathsIdentical(old_path, from_loc_maybe_null)) { - throw new RuntimeException("assert failure: old_path=" + old_path + ", from_loc=" + from_loc); + throw new RuntimeException("assert failure: old_path=" + old_path + ", from_loc=" + from_loc_maybe_null); } //System.out.println( " adding " + old_path + " -> null" ); @@ -2112,13 +2129,9 @@ public void perform(TimerEvent event){ }else{ - String from_loc_to_use = FileUtil.translateMoveFilePath( old_path_str, - new_path_str, from_loc.getAbsolutePath() ); - - if ( from_loc_to_use == null ){ - - return; - } + String from_loc_to_use_maybe_null = + from_loc_maybe_null==null?null:FileUtil.translateMoveFilePath( old_path_str, + new_path_str, from_loc_maybe_null.getAbsolutePath() ); String to_loc_to_use = FileUtil.translateMoveFilePath( old_path_str, new_path_str, to_loc.getAbsolutePath() ); @@ -2126,13 +2139,13 @@ public void perform(TimerEvent event){ // delete old from_indexes.add( file_index ); - from_links.add( from_loc ); + from_links.add( from_loc_maybe_null ); to_links.add( null ); // add new from_indexes.add( file_index ); - from_links.add( FileUtil.newFile(from_loc_to_use)); + from_links.add( from_loc_to_use_maybe_null==null?null:FileUtil.newFile(from_loc_to_use_maybe_null)); to_links.add( to_loc_to_use == null ? to_loc : FileUtil.newFile(to_loc_to_use)); } } diff --git a/core/src/com/biglybt/core/download/impl/DownloadManagerStateImpl.java b/core/src/com/biglybt/core/download/impl/DownloadManagerStateImpl.java index 7c6d8a53e09..15475497cd9 100644 --- a/core/src/com/biglybt/core/download/impl/DownloadManagerStateImpl.java +++ b/core/src/com/biglybt/core/download/impl/DownloadManagerStateImpl.java @@ -2342,7 +2342,7 @@ public void setPrimaryFile(DiskManagerFileInfo dmfi) { { LinkFileMap links = getFileLinks(); - File existing = (File)links.get( source_index, link_source); + File existing = (File)links.get( source_index ); if ( link_destination == null ){ @@ -2350,7 +2350,7 @@ public void setPrimaryFile(DiskManagerFileInfo dmfi) { return; } - }else if ( existing != null && existing.getAbsolutePath().equals( link_destination.getAbsolutePath())){ + }else if ( existing != null && FileUtil.areFilePathsIdentical( existing, link_destination )){ return; } @@ -2373,20 +2373,20 @@ public void setPrimaryFile(DiskManagerFileInfo dmfi) { public void setFileLinks( List source_indexes, - List link_sources, + List link_sources_may_have_nulls, List link_destinations ) { LinkFileMap links = getFileLinks(); boolean changed = false; - for ( int i=0;i it = links.entryIterator(); + if ( links.size() > 0 ){ - boolean changed = false; - - while( it.hasNext()){ - - LinkFileMap.Entry entry = it.next(); + links.clear(); - StringInterner.FileKey target = entry.getToFile(); - - if ( target != null ){ - - links.put( entry.getIndex(), entry.getFromFile(), null ); - - changed = true; - } - } - - if ( changed ){ - synchronized( this ){ file_link_cache = new WeakReference<>( links ); @@ -2481,7 +2468,7 @@ public void setPrimaryFile(DiskManagerFileInfo dmfi) { } } - File res = map.get( source_index, link_source ); + File res = map.get( source_index ); //System.out.println( "getFileLink: " + link_source + " -> " + res ); @@ -2531,35 +2518,11 @@ public void setPrimaryFile(DiskManagerFileInfo dmfi) { if ( new_values.size() > 0 ){ res.fromBencodeObject( new_values ); - - }else{ - - List old_values = getListAttributeSupport( AT_FILE_LINKS_DEPRECATED, false ); - - for (int i=0;i source_indexes, - List link_sources, + List link_sources_may_have_nulls, List link_destinations ) { } diff --git a/core/src/com/biglybt/core/lws/LWSDiskManagerState.java b/core/src/com/biglybt/core/lws/LWSDiskManagerState.java index cb349eff417..68fd9dbe3ad 100644 --- a/core/src/com/biglybt/core/lws/LWSDiskManagerState.java +++ b/core/src/com/biglybt/core/lws/LWSDiskManagerState.java @@ -483,7 +483,7 @@ public void removeAttribute(String name){} public void setFileLinks( List source_indexes, - List link_sources, + List link_sources_may_have_nulls, List link_destinations ) { } diff --git a/core/src/com/biglybt/core/util/FileUtil.java b/core/src/com/biglybt/core/util/FileUtil.java index 11f82f897ca..06efa62ecd4 100644 --- a/core/src/com/biglybt/core/util/FileUtil.java +++ b/core/src/com/biglybt/core/util/FileUtil.java @@ -467,6 +467,16 @@ public static boolean recursiveDeleteNoCheck(File f) { File f, Set ignore_set, boolean log_warnings ) + { + recursiveEmptyDirDelete( f, ignore_set, 1, log_warnings ); + } + + private static void + recursiveEmptyDirDelete( + File f, + Set ignore_set, + int level, + boolean log_warnings ) { if ( f == null ){ @@ -511,7 +521,7 @@ public static boolean recursiveDeleteNoCheck(File f) { if ( files == null ){ if (log_warnings ){ - Debug.out("Empty folder delete: failed to list contents of directory " + f ); + Debug.out("Empty folder delete: failed to list contents of directory '" + f + "'"); } return; @@ -519,13 +529,16 @@ public static boolean recursiveDeleteNoCheck(File f) { boolean hasIgnoreSet = ignore_set.size() > 0; + List sub_dirs = new ArrayList<>(); + List files_inside = new ArrayList<>(); + for (int i = 0; i < files.length; i++) { File x = files[i]; if ( x.isDirectory()){ - recursiveEmptyDirDelete(files[i],ignore_set,log_warnings); + sub_dirs.add( x ); }else{ @@ -533,52 +546,75 @@ public static boolean recursiveDeleteNoCheck(File f) { if ( !x.delete()){ + files_inside.add( x ); + if ( log_warnings ){ - Debug.out("Empty folder delete: failed to delete file " + x ); + + Debug.out("Empty folder delete: failed to delete file '" + x + "'"); } } + }else{ + + files_inside.add( x ); } } } + + if ( level == 1 && !files_inside.isEmpty()){ + + if ( log_warnings ){ + Debug.out( "Empty folder delete: abandoning as top level folder '" + f + "' isn't empty" ); + } + + return; + } + + for ( File dir: sub_dirs ){ + + recursiveEmptyDirDelete(dir,ignore_set,level+1,log_warnings); + } - if ( moveToDirFile != null && - ( areFilePathsIdentical( f.getAbsoluteFile(), moveToDirFile) || f.getCanonicalPath().equals(moveToDir))){ + if ( moveToDirFile != null && ( areFilePathsIdentical( f.getAbsoluteFile(), moveToDirFile) || f.getCanonicalPath().equals(moveToDir))){ if ( log_warnings ){ - Debug.out("Empty folder delete: not allowed to delete the MoveTo dir !"); + Debug.out("Empty folder delete: not allowed to delete the Move-To dir!"); } return; } - if ( defSaveDirFile != null && - ( areFilePathsIdentical( f.getAbsoluteFile(), defSaveDirFile) ||f.getCanonicalPath().equals(defSaveDir))){ + if ( defSaveDirFile != null && ( areFilePathsIdentical( f.getAbsoluteFile(), defSaveDirFile) ||f.getCanonicalPath().equals(defSaveDir))){ if ( log_warnings ){ - Debug.out("Empty folder delete: not allowed to delete the default data dir !"); + Debug.out("Empty folder delete: not allowed to delete the Default Data dir!"); } return; } - File[] files_inside = f.listFiles(); + files = f.listFiles(); - if ( files_inside == null ){ + if ( files == null ){ if (log_warnings ){ - Debug.out("Empty folder delete: failed to list contents of directory " + files_inside ); + Debug.out("Empty folder delete: failed to list contents of directory '" + f + "'" ); } - }else if ( files_inside.length == 0 ){ + + return; + } + + if ( files.length == 0 ){ if ( !f.delete()){ if ( log_warnings ){ - Debug.out("Empty folder delete: failed to delete directory " + f ); + Debug.out("Empty folder delete: failed to delete directory '" + f + "'"); } } }else{ + if ( log_warnings ){ - Debug.out("Empty folder delete: " + files_inside.length + " file(s)/folder(s) still in \"" + f + "\" - first listed item is \"" + files_inside[0].getName() + "\". Not removing."); + Debug.out("Empty folder delete: " + files.length + " file(s)/folder(s) still in '" + f + "' - first listed item is '" + files[0].getName() + "'. Not removing."); } } } diff --git a/core/src/com/biglybt/core/util/LinkFileMap.java b/core/src/com/biglybt/core/util/LinkFileMap.java index 7cb7cfaa677..68da3ab1945 100644 --- a/core/src/com/biglybt/core/util/LinkFileMap.java +++ b/core/src/com/biglybt/core/util/LinkFileMap.java @@ -21,6 +21,7 @@ import java.io.File; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import com.biglybt.core.util.StringInterner.FileKey; @@ -45,15 +46,20 @@ * Note that the FileManagerImpl's getLink requires access to the from-name to verify that the link it looks up * is valid (principally caused by the 'move' method running BEFORE links are updated - really this should be * reworked + * + * From 3701 the FileKey has been introduced to reduce memory usage of full file name storage + * Also we never write entries for deleted links (target=null) and treat source=dest as deleted link */ - private final Map name_map = new HashMap<>(); - private final Map index_map = new HashMap<>(); + // private final Map name_map = new HashMap<>(); removed 3700 + + private static final boolean STORE_FROM_KEY = true; // can set false from 3801+ and users can still revert to 3800 if wanted (but not earlier...) + + private final Map index_map = new ConcurrentHashMap<>(); public File get( - int index, - File from_file ) + int index ) { if ( index >= 0 ){ @@ -67,37 +73,15 @@ } }else{ - // just an old link pre-migration - // Debug.out( "unexpected index: " + index ); + Debug.out( "unexpected index: " + index ); } - Entry entry = name_map.get( new FileKey( from_file )); - - if ( entry == null ){ - - return( null ); - - }else{ - - // migration - all existing links to migrate have an index of -1 - - int e_index = entry.getIndex(); - - if ( e_index >= 0 && e_index != index ){ - - return( null ); - } - - FileKey to_fk = entry.getToFile(); - - return( to_fk==null?null:to_fk.getFile()); - } + return( null ); } public Entry getEntry( - int index, - File from_file ) + int index ) { if ( index >= 0 ){ @@ -112,160 +96,61 @@ Debug.out( "unexpected index" ); } - Entry entry = name_map.get( new FileKey( from_file )); - - if ( entry == null ){ - - return( null ); - - }else{ - - // migration - all existing links to migrate have an index of -1 - - int e_index = entry.getIndex(); - - if ( e_index >= 0 && e_index != index ){ - - return( null ); - } - - return( entry ); - } + return( null ); } public void put( int index, - FileKey from_fk, + FileKey from_fk_maybe_null, FileKey to_fk ) - { - Entry entry = new Entry( index, from_fk, to_fk ); - - if ( index >= 0 ){ - - index_map.put( index, entry ); - - // remove any legacy entry - - if ( name_map.size() > 0 ){ - - name_map.remove( from_fk ); - } + { + if ( to_fk == null || ( from_fk_maybe_null != null && from_fk_maybe_null.equals(to_fk))){ + + index_map.remove( index ); + }else{ - - Entry existing = name_map.get( from_fk ); - - if ( existing == null || - !existing.getFromFile().equals( from_fk) || - !existing.getToFile().equals( to_fk )){ - + + Entry entry = new Entry( index, from_fk_maybe_null, to_fk ); + + if ( index >= 0 ){ + + index_map.put( index, entry ); + + }else{ + Debug.out( "unexpected index" ); } - - name_map.put( from_fk, entry ); } } public void - putMigration( - FileKey from_file, - FileKey to_file ) + clear() { - Entry entry = new Entry( -1, from_file, to_file ); - - name_map.put( from_file, entry ); + index_map.clear(); } - - public void - remove( - int index, - FileKey key ) - { - if ( index >= 0 ){ - - index_map.remove( index ); - - }else{ - // this can happen when removing non-resolved entries, not a problem - //Debug.out( "unexpected index" ); - } - - if ( name_map.size() > 0 ){ - - name_map.remove( key ); - } - } - - public boolean - hasLinks() + + public Iterator + entryIterator() { - for (Entry entry: index_map.values()){ - - FileKey to_file = entry.getToFile(); - - if ( to_file != null ){ - - if ( !entry.getFromFile().equals( to_file )){ - - return( true ); - } - } - } - - return( false ); + return( index_map.values().iterator()); } - + public int size() { - int size = 0; - - for (Entry entry: index_map.values()){ - - FileKey to_file = entry.getToFile(); - - if ( to_file != null ){ - - if ( !entry.getFromFile().equals( to_file )){ - - size++; - } - } - } - - return( size ); - } - - public boolean - isEmpty() - { - return( index_map.isEmpty()); - } - - public Iterator - entryIterator() - { - if ( index_map.size() > 0 ){ - - if ( name_map.size() == 0 ){ - - return( index_map.values().iterator()); - } - - Set entries = new HashSet<>(index_map.values()); - - entries.addAll( name_map.values()); - - return( entries.iterator()); - } - - return( name_map.values().iterator()); + return( index_map.size()); } @Override public Object toBencodeObject() { + // I'd like to add some common-prefix optimisations to this encoding but this + // would break backwards compatibility so it would need to be added in parallel + // to the existing crap method for a few releases before actually switching + // which would make things worse for a while and I can't summon the strength + List list = new ArrayList<>(); Iterator it = entryIterator(); @@ -276,12 +161,21 @@ int index = entry.getIndex(); - StringInterner.FileKey source = entry.getFromFile(); + StringInterner.FileKey source_maybe_null = entry.getFromFileMaybeNull(); + StringInterner.FileKey target = entry.getToFile(); - String str = index + "\n" + source + "\n" + (target==null?"":target.toString()); - - list.add( str ); + StringBuilder str = new StringBuilder( 512 ); + + str.append( index ); + str.append( "\n" ); + if ( source_maybe_null != null ){ + str.append( source_maybe_null.toString()); + } + str.append( "\n" ); + str.append( target.toString()); + + list.add( str.toString()); } return( list ); @@ -302,18 +196,30 @@ try{ int index = Integer.parseInt( bits[0].trim()); - StringInterner.FileKey source = new StringInterner.FileKey( (bits[1])); - StringInterner.FileKey target = bits.length<3?null:new StringInterner.FileKey((bits[2])); + String from_str = bits[1]; + + StringInterner.FileKey source_maybe_null = from_str.isEmpty()?null: new StringInterner.FileKey( from_str ); + + StringInterner.FileKey target; + + if ( bits.length < 3 ){ + + target = null; + + }else{ + + String to_str = bits[2]; + + target = new StringInterner.FileKey( to_str ); + } if( index >= 0 ){ - put( index, source, target ); + put( index, source_maybe_null, target ); }else{ - - // can get here when partially resolved link state is saved and then re-read - - putMigration( source, target ); + + Debug.out( "unexpected index" ); } }catch( Throwable e ){ @@ -340,18 +246,6 @@ str += "i_map={ " + i_str + " }"; } - if ( name_map.size() > 0 ){ - - String n_str = ""; - - for ( Entry e: name_map.values()){ - - n_str += (n_str.length()==0?"":", ") + e.getString(); - } - - str += "n_map={ " + n_str + " }"; - } - return( str ); } @@ -359,18 +253,18 @@ Entry { private final int index; - private final FileKey from_file; + private final FileKey from_file_maybe_null; private final FileKey to_file; private Entry( int _index, - FileKey _from_file, + FileKey _from_file_maybe_null, FileKey _to_file ) { - index = _index; - from_file = _from_file; - to_file = _to_file; + index = _index; + from_file_maybe_null = STORE_FROM_KEY?_from_file_maybe_null:null; + to_file = _to_file; } public int @@ -380,9 +274,9 @@ } public FileKey - getFromFile() + getFromFileMaybeNull() { - return( from_file ); + return( from_file_maybe_null ); } public FileKey @@ -394,7 +288,7 @@ public String getString() { - return( index + ": " + from_file + " -> " + to_file ); + return( index + ": " + from_file_maybe_null + " -> " + to_file ); } } } diff --git a/core/src/com/biglybt/ui/common/table/impl/TableViewImpl.java b/core/src/com/biglybt/ui/common/table/impl/TableViewImpl.java index da4f5b80811..420712131ac 100644 --- a/core/src/com/biglybt/ui/common/table/impl/TableViewImpl.java +++ b/core/src/com/biglybt/ui/common/table/impl/TableViewImpl.java @@ -2681,70 +2681,74 @@ private void setSelectedRows(final TableRowCore[] newSelectionArray, System.out.println(" via " + Debug.getCompressedStackTrace(4)); /**/ - final List oldSelectionList = new ArrayList<>(); - - LinkedHashSet listNewlySelected; + Set oldSelectionSet = new LinkedHashSet<>(); + Set newlySelectedSet = new LinkedHashSet<>(); + boolean somethingChanged; + synchronized (rows_sync) { if (selectedRows.size() == 0 && newSelectionArray.length == 0) { return; } - oldSelectionList.addAll(selectedRows); + oldSelectionSet.addAll(selectedRows); + listSelectedCoreDataSources = null; + selectedRows.clear(); - listNewlySelected = new LinkedHashSet<>(); - // We'll remove items still selected from oldSelectionLeft, leaving // it with a list of items that need to fire the deselection event. + for (TableRowCore row : newSelectionArray) { + if (row == null || row.isRowDisposed()) { continue; } - boolean existed = false; - for (TableRowCore oldRow : oldSelectionList) { - if (oldRow == row) { - existed = true; - if (!selectedRows.contains(row)) { - selectedRows.add(row); - } - oldSelectionList.remove(row); - break; + if ( oldSelectionSet.contains( row )){ + + if (!selectedRows.contains(row)){ + + selectedRows.add(row); } - } - if (!existed) { - if (!selectedRows.contains(row)) { + + oldSelectionSet.remove(row); + + }else{ + + if (!selectedRows.contains(row)){ + selectedRows.add(row); } - if (!listNewlySelected.contains(row)) { - listNewlySelected.add(row); + + if (!newlySelectedSet.contains(row)) { + + newlySelectedSet.add(row); } } } - somethingChanged = listNewlySelected.size() > 0 - || oldSelectionList.size() > 0; + somethingChanged = newlySelectedSet.size() > 0 || oldSelectionSet.size() > 0; if ( somethingChanged && updateHistory ){ addHistory( new HistorySelection( - oldSelectionList.toArray( new TableRowCore[ oldSelectionList.size()]), + oldSelectionSet.toArray( new TableRowCore[ oldSelectionSet.size()]), selectedRows.toArray( new TableRowCore[ selectedRows.size()]))); } if (DEBUG_SELECTION) { System.out.println(somethingChanged + "] +" - + listNewlySelected.size() + "/-" + oldSelectionList.size() + + newlySelectedSet.size() + "/-" + oldSelectionSet.size() + "; UpdateSelectedRows via " + Debug.getCompressedStackTrace()); } } if (somethingChanged) { - TableRowCore[] selected = listNewlySelected.toArray(new TableRowCore[0]); - TableRowCore[] deselected = oldSelectionList.toArray(new TableRowCore[0]); + TableRowCore[] selected = newlySelectedSet.toArray(new TableRowCore[0]); + TableRowCore[] deselected = oldSelectionSet.toArray(new TableRowCore[0]); uiSelectionChanged( selected, deselected ); @@ -2754,11 +2758,11 @@ private void setSelectedRows(final TableRowCore[] newSelectionArray, } if (trigger && somethingChanged) { - if (listNewlySelected.size() > 0) { - triggerSelectionListeners(listNewlySelected.toArray(new TableRowCore[0])); + if (newlySelectedSet.size() > 0) { + triggerSelectionListeners(newlySelectedSet.toArray(new TableRowCore[0])); } - if (oldSelectionList.size() > 0) { - triggerDeselectionListeners(oldSelectionList.toArray(new TableRowCore[0])); + if (oldSelectionSet.size() > 0) { + triggerDeselectionListeners(oldSelectionSet.toArray(new TableRowCore[0])); } triggerTabViewsDataSourceChanged(); diff --git a/uis/src/com/biglybt/ui/swt/TorrentMenuFancy.java b/uis/src/com/biglybt/ui/swt/TorrentMenuFancy.java index cd9696eb628..150b0d82ca5 100644 --- a/uis/src/com/biglybt/ui/swt/TorrentMenuFancy.java +++ b/uis/src/com/biglybt/ui/swt/TorrentMenuFancy.java @@ -1308,7 +1308,7 @@ public void buildMenu(Menu menu) { if ( stopped && !hasClearableLinks ){ TOTorrent torrent = dm.getTorrent(); if ( torrent != null && !torrent.isSimpleTorrent()){ - if ( dms.getFileLinks().hasLinks()){ + if ( dms.getFileLinks().size() > 0){ hasClearableLinks = true; } } @@ -1399,7 +1399,7 @@ public void run(DownloadManager[] dms) public void run(DownloadManager dm) { if ( ManagerUtils.isStopped(dm) && - dm.getDownloadState().getFileLinks().hasLinks()){ + dm.getDownloadState().getFileLinks().size() > 0){ DiskManagerFileInfoSet fis = dm.getDiskManagerFileInfoSet(); diff --git a/uis/src/com/biglybt/ui/swt/TorrentUtil.java b/uis/src/com/biglybt/ui/swt/TorrentUtil.java index 662f93e9687..f4ac80734eb 100644 --- a/uis/src/com/biglybt/ui/swt/TorrentUtil.java +++ b/uis/src/com/biglybt/ui/swt/TorrentUtil.java @@ -719,7 +719,7 @@ public static void fillTorrentMenu(final Menu menu, if ( torrent != null && !torrent.isSimpleTorrent()){ - if ( dm_state.getFileLinks().hasLinks()){ + if ( dm_state.getFileLinks().size() > 0){ hasClearableLinks = true; } @@ -1183,7 +1183,7 @@ public void run(DownloadManager[] dms) @Override public void run(DownloadManager dm) { - if ( ManagerUtils.isStopped(dm) && dm.getDownloadState().getFileLinks().hasLinks()){ + if ( ManagerUtils.isStopped(dm) && dm.getDownloadState().getFileLinks().size()>0){ DiskManagerFileInfoSet fis = dm.getDiskManagerFileInfoSet();