From 727816a683e9b503fe77c822c388570e11ac165c Mon Sep 17 00:00:00 2001
From: Ann Priestman <apriestman@basistech.com>
Date: Thu, 24 May 2018 09:25:43 -0400
Subject: [PATCH] Switch to map of SparseBitSet. Cleanup

---
 .../sleuthkit/datamodel/SleuthkitCase.java    | 99 ++++++++++++-------
 1 file changed, 61 insertions(+), 38 deletions(-)

diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
index cafc81b15..380a652ee 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -122,8 +122,7 @@ public class SleuthkitCase {
 	private Map<Integer, BlackboardAttribute.Type> typeIdToAttributeTypeMap;
 	private Map<String, BlackboardArtifact.Type> typeNameToArtifactTypeMap;
 	private Map<String, BlackboardAttribute.Type> typeNameToAttributeTypeMap;
-	//private Set<Long> hasChildren;
-	private SparseBitSet hasChildrenBitSet;
+	private Map<Long, SparseBitSet> hasChildrenBitSetMap;
 
 	private long nextArtifactId; // Used to ensure artifact ids come from the desired range.
 	// This read/write lock is used to implement a layer of locking on top of
@@ -277,47 +276,39 @@ private void init() throws Exception {
 		initIngestStatusTypes(connection);
 		initReviewStatuses(connection);
 		initEncodingTypes(connection);
-
-		
-		long timestamp = System.currentTimeMillis();
-		//hasChildren = new HashSet<Long>();
-		hasChildrenBitSet = new SparseBitSet(); // Could query for current highest obj id
-		Statement statement = null;
-		ResultSet resultSet = null;
-		acquireSingleUserCaseWriteLock();
-		try {
-			statement = connection.createStatement();
-			try {
-				//resultSet = statement.executeQuery("select distinct obj_id as par_obj_id from blackboard_artifacts where artifact_type_id = 13 or artifact_type_id = 24 union select distinct par_obj_id from tsk_objects where type=4"); //NON-NLS
-				resultSet = statement.executeQuery("select distinct par_obj_id from tsk_objects"); //NON-NLS
-				while(resultSet.next()) {
-					//hasChildren.add(resultSet.getLong("par_obj_id"));
-					hasChildrenBitSet.set((int)(resultSet.getLong("par_obj_id")));
-				}
-			} catch (SQLException ex) {//NON-NLS
-				
-				resultSet.close();
-				resultSet = null;
-			}
-		} finally {
-			closeResultSet(resultSet);
-			closeStatement(statement);
-			releaseSingleUserCaseWriteLock();
-		}
-		//System.out.println("\n###### Number nodes with children: " + hasChildren.size());
-		System.out.println("\n###### Bit set stats: " + hasChildrenBitSet.statistics());
-		long delay = System.currentTimeMillis() - timestamp;
-		System.out.println("   Elapsed milliseconds: " + delay);
-		
-
+		initHasChildrenMap(connection);
 		connection.close();
 	}
 	
 	public boolean getHasChildren(Long objId) {
 		//return hasChildren.contains(objId);
-		return hasChildrenBitSet.get(objId.intValue());
+		//return hasChildrenBitSet.get(objId.intValue());
+		long mapIndex = objId / Integer.MAX_VALUE;
+		int mapValue = (int) (objId % Integer.MAX_VALUE);
+		
+		synchronized(this) {
+			if (hasChildrenBitSetMap.containsKey(mapIndex)) {
+				return hasChildrenBitSetMap.get(mapIndex).get(mapValue);
+			}
+			return false;
+		}
 	}
-
+	
+	private void setHasChildren(Long objId) {
+		long mapIndex = objId / Integer.MAX_VALUE;
+		int mapValue = (int) (objId % Integer.MAX_VALUE);
+		
+		synchronized(this) {
+			if (hasChildrenBitSetMap.containsKey(mapIndex)) {
+				hasChildrenBitSetMap.get(mapIndex).set(mapValue);
+			} else {
+				SparseBitSet bitSet = new SparseBitSet();
+				bitSet.set(mapValue);
+				hasChildrenBitSetMap.put(mapIndex, bitSet);
+			}
+		}
+	}
+	
 	/**
 	 * Returns an instance of CommunicationsManager
 	 *
@@ -572,6 +563,28 @@ private void initEncodingTypes(CaseDbConnection connection) throws SQLException,
 			releaseSingleUserCaseWriteLock();
 		}
 	}
+	
+	private void initHasChildrenMap(CaseDbConnection connection) throws SQLException, TskCoreException {
+		long timestamp = System.currentTimeMillis();
+		hasChildrenBitSetMap = new HashMap<Long, SparseBitSet>();
+		
+		Statement statement = null;
+		ResultSet resultSet = null;
+		acquireSingleUserCaseWriteLock();
+		try {
+			statement = connection.createStatement();
+			resultSet = statement.executeQuery("select distinct par_obj_id from tsk_objects"); //NON-NLS
+			while(resultSet.next()) {
+				setHasChildren(resultSet.getLong("par_obj_id"));
+			}
+			long delay = System.currentTimeMillis() - timestamp;
+			logger.log(Level.INFO, "Time to initialize parent node cache: {0} ms", delay); //NON-NLS
+		} finally {
+			closeResultSet(resultSet);
+			closeStatement(statement);
+			releaseSingleUserCaseWriteLock();
+		}
+	}
 
 	/**
 	 * Modify the case database to bring it up-to-date with the current version
@@ -4464,6 +4477,15 @@ public VirtualDirectory addVirtualDirectory(long parentId, String directoryName)
 		}
 	}
 	
+	/**
+	 * Add an object to the tsk_objects table. 
+	 * Returns the object ID for the new object.
+	 * @param parentId   Parent of the new object
+	 * @param objectType Type of the new object
+	 * @param connection Case connection
+	 * @return the object ID for the new object
+	 * @throws SQLException 
+	 */
 	private long addObject(long parentId, int objectType, CaseDbConnection connection) throws SQLException {
 		ResultSet resultSet = null;
 		acquireSingleUserCaseWriteLock();
@@ -4483,7 +4505,8 @@ private long addObject(long parentId, int objectType, CaseDbConnection connectio
 			if(resultSet.next()) {
 				if(parentId != 0) {
 					//this.hasChildren.add(parentId);
-					this.hasChildrenBitSet.set((int)parentId);
+					//this.hasChildrenBitSet.set((int)parentId);
+					setHasChildren(parentId);
 				}
 				return resultSet.getLong(1); //last_insert_rowid()
 			} else {
-- 
GitLab