package lobstack; import java.util.TreeMap; import java.util.Map; import java.util.TreeSet; import java.util.SortedMap; import java.nio.ByteBuffer; import org.junit.Assert; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.AbstractMap.SimpleEntry; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.Executor; import jelectrum.TimeRecord; public class LobstackNode implements java.io.Serializable { // If anything else is added, serialize and deserialize need to be updated private String prefix; private TreeMap<String, NodeEntry> children; public static int NODE_VERSION=-2; public LobstackNode(String prefix) { this.prefix = prefix; children = new TreeMap<String, NodeEntry>(); } public LobstackNode(String prefix, TreeMap<String, NodeEntry> children) { this.prefix = prefix; this.children = children; } public void printTree(Lobstack stack) throws IOException { System.out.print(prefix + " - " + children.size()); System.out.println(); for(String key : children.keySet()) { NodeEntry ne = children.get(key); if (ne.node) { stack.loadNodeAt(ne.location).printTree(stack); } else { System.out.println(key + " data @" + ne.location + " - bytes:" + stack.loadAtLocation(ne.location).capacity()); } } } public void getTreeStats(Lobstack stack, TreeStat stats) throws IOException { synchronized(stats) { stats.node_children+=children.size(); stats.node_children_min = Math.min(stats.node_children_min, children.size()); stats.node_children_max = Math.max(stats.node_children_max, children.size()); } /*if (children.size() > 256) { System.out.println("" + children.size() + " - " + prefix + ": " + children.keySet()); }*/ for(String key : children.keySet()) { NodeEntry ne = children.get(key); int sz = stack.loadSizeAtLocation(ne.location); stats.addFileUse(ne.location, sz + 4); if (ne.node) { LobstackNode n = stack.loadNodeAt(ne.location); synchronized(stats) { stats.node_size += sz + 4; stats.node_count++; } n.getTreeStats(stack, stats); } else { synchronized(stats) { stats.data_count++; stats.data_size+= sz + 4; } } } } public void getAll(Lobstack stack, BlockingQueue<Map.Entry<String, ByteBuffer> > consumer) throws IOException, InterruptedException { for(String key : children.keySet()) { NodeEntry ne = children.get(key); if (ne.node) { stack.loadNodeAt(ne.location).getAll(stack, consumer); } else { String data_key = key.substring(0, key.length()-1); consumer.put(new SimpleEntry<String,ByteBuffer>(data_key, stack.loadAtLocation(ne.location))); } } } /** * Consider all repositions of files less than or equal to max_file */ public TreeMap<Integer, Long> estimateReposition(Lobstack stack, int max_file) throws IOException { TreeMap<Integer, Long> sz_map = new TreeMap<Integer, Long>(); TreeMap<String, WorkUnit> work_map = new TreeMap<String, WorkUnit>(); for(Map.Entry<String, NodeEntry> me : children.entrySet()) { String str = me.getKey(); NodeEntry ne = me.getValue(); if (ne.min_file_number < max_file) { long sz = stack.loadSizeAtLocation(ne.location) + 4; for(int idx = (int) (ne.location / Lobstack.SEGMENT_FILE_SIZE) + 1; idx<=max_file; idx++) { TreeUtil.addItem(sz_map, idx, sz); } if (ne.node) { LobstackNode n = stack.loadNodeAt(ne.location); WorkUnit wu = new WorkUnit(stack,n, max_file); if (stack.getQueue().offer(wu)) { work_map.put(str, wu); } else { TreeUtil.addTree(sz_map, n.estimateReposition(stack, max_file)); } } } } for(Map.Entry<String, WorkUnit> me : work_map.entrySet()) { TreeUtil.addTree(sz_map, me.getValue().estimate.get()); } return sz_map; } public NodeEntry reposition(Lobstack stack, TreeMap<Long, ByteBuffer> save_entries, int min_file) throws IOException { TreeMap<String, WorkUnit> work_map = new TreeMap<String, WorkUnit>(); for(Map.Entry<String, NodeEntry> me : children.entrySet()) { String str = me.getKey(); NodeEntry ne = me.getValue(); if (ne.min_file_number < min_file) { if (ne.node) { LobstackNode n = loadNode(stack, save_entries, ne.location); WorkUnit wu = new WorkUnit(stack, n, min_file, save_entries); if (stack.getQueue().offer(wu)) { work_map.put(str, wu); } else { ne = n.reposition(stack, save_entries, min_file); children.put(str, ne); } } else { ByteBuffer data = stack.loadAtLocation(ne.location); ByteBuffer comp = stack.compress(data); ne.location = stack.allocateSpace(comp.capacity()); ne.min_file_number = (int)(ne.location / Lobstack.SEGMENT_FILE_SIZE); synchronized(save_entries) { save_entries.put(ne.location, comp); } children.put(str, ne); } } } for(Map.Entry<String, WorkUnit> me : work_map.entrySet()) { NodeEntry ne = me.getValue().return_entry.get(); children.put(me.getKey(), ne); } ByteBuffer self_buffer = serialize(); ByteBuffer comp = stack.compress(self_buffer); long self_loc = stack.allocateSpace(comp.capacity()); synchronized(save_entries) { save_entries.put(self_loc, comp); } NodeEntry my_entry = new NodeEntry(); my_entry.node = true; my_entry.location = self_loc; my_entry.min_file_number = (int) (self_loc / Lobstack.SEGMENT_FILE_SIZE); for(NodeEntry ne : children.values()) { my_entry.min_file_number = Math.min(my_entry.min_file_number, ne.min_file_number); } return my_entry; } public int getMinFileNumber(long location) { int min_file_number = (int) (location / Lobstack.SEGMENT_FILE_SIZE); for(NodeEntry ne : children.values()) { min_file_number = Math.min(min_file_number, ne.min_file_number); } return min_file_number; } public NodeEntry putAll(Lobstack stack, TreeMap<Long, ByteBuffer> save_entries, Map<String, NodeEntry> put_map) throws IOException { TimeRecord tr = stack.getTimeReport(); // Add all as direct children children.putAll(put_map); TreeMap<String, WorkUnit> working_map = new TreeMap<String, WorkUnit>(); for(Map.Entry<String, NodeEntry> me : children.entrySet()) { working_map.put(me.getKey(), new WorkUnit(stack, me.getValue(), save_entries)); } //Just to make sure they are all from new entries children.clear(); boolean keep_looking=true; while(keep_looking) { keep_looking=false; ArrayList<String> lst = new ArrayList<String>(); lst.addAll(working_map.keySet()); for(int i=0; i<lst.size()-1; i++) { String a = lst.get(i); String b = lst.get(i+1); NodeEntry ne_a = working_map.get(a).ne; NodeEntry ne_b = working_map.get(b).ne; WorkUnit wu_a = working_map.get(a); WorkUnit wu_b = working_map.get(b); // If b should go into a, and a is a node // just add it if ((b.startsWith(a)) && (ne_a.node)) { TreeSet<String> rm_lst = new TreeSet<String>(); for(int j=i+1; j<lst.size(); j++) { String c = lst.get(j); WorkUnit wu_c = working_map.get(c); NodeEntry ne_c = wu_c.ne; if (c.startsWith(a)) { rm_lst.add(c); if (ne_c.node) { if (ne_c.location == -1) { //C must be a node we just added Assert.assertEquals("C must be just added node", -1,ne_c.location); wu_a.put_map.putAll(wu_c.put_map); } else { // C must be existing, add it as a sub wu_a.put_map.put(c, ne_c); // Anything we want to add to C should be added to this new common instead wu_a.put_map.putAll(wu_c.put_map); } } else { wu_a.put_map.put(c, ne_c); } } } for(String s : rm_lst) { working_map.remove(s); } keep_looking=true; break; } if (working_map.size() > 8) { int common = commonLength(stack, a, b) - prefix.length(); if (common > 0) { String common_prefix = a.substring(0, common + prefix.length()); TreeSet<String> rm_lst = new TreeSet<String>(); WorkUnit wu_common = new WorkUnit(stack, common_prefix, save_entries); for(int j=i+1; j<lst.size(); j++) { String c = lst.get(j); if (c.startsWith(common_prefix)) { WorkUnit wu_c = working_map.get(c); NodeEntry ne_c = wu_c.ne; if (ne_c.node) { if (ne_c.location == -1) { //C must be a node we just added Assert.assertEquals("C must be just added node", -1,ne_c.location); wu_common.put_map.putAll(wu_c.put_map); } else { // C must be existing, add it as a sub wu_common.put_map.put(c, ne_c); // Anything we want to add to C should be added to this new common instead wu_common.put_map.putAll(wu_c.put_map); } } else { wu_common.put_map.put(c, ne_c); } rm_lst.add(c); } } for(String s : rm_lst) { working_map.remove(s); } working_map.put(common_prefix, wu_common); keep_looking=true; break; } } } } for(Map.Entry<String, WorkUnit> me : working_map.entrySet()) { WorkUnit wu = me.getValue(); wu.assertConsistentForPut(); //Load node as needed if ((wu.ne.node) && (wu.put_map.size() > 0) && (wu.node==null)) { wu.node = loadNode(stack, save_entries, wu.ne.location); } if (wu.put_map.size() > 0) { if (!stack.getQueue().offer(wu)) { NodeEntry ne = wu.node.putAll(stack, wu.save_entries, wu.put_map); wu.return_entry.setResult(ne); } } } for(Map.Entry<String, WorkUnit> me : working_map.entrySet()) { WorkUnit wu = me.getValue(); if (wu.put_map.size() > 0) { children.put(me.getKey(), wu.return_entry.get()); } else { children.put(me.getKey(), wu.ne); } } long t1_serialize = System.nanoTime(); ByteBuffer self_buffer = serialize(); tr.addTime(System.nanoTime() - t1_serialize, "serialize"); long t1_comp = System.nanoTime(); ByteBuffer comp = stack.compress(self_buffer); tr.addTime(System.nanoTime() - t1_comp, "compress"); long t1_allocate = System.nanoTime(); long self_loc = stack.allocateSpace(comp.capacity()); tr.addTime(System.nanoTime() - t1_allocate, "alloc"); synchronized(save_entries) { save_entries.put(self_loc, comp); } NodeEntry my_entry = new NodeEntry(); my_entry.node = true; my_entry.location = self_loc; my_entry.min_file_number = (int) (self_loc / Lobstack.SEGMENT_FILE_SIZE); for(NodeEntry ne : children.values()) { my_entry.min_file_number = Math.min(my_entry.min_file_number, ne.min_file_number); } return my_entry; } private LobstackNode loadNode(Lobstack stack, TreeMap<Long, ByteBuffer> save_entries, long location) throws IOException { LobstackNode n = null; synchronized(save_entries) { if (save_entries.containsKey(location)) { n = LobstackNode.deserialize(stack.decompress(save_entries.get(location))); throw new RuntimeException("whatfuck"); } } long t1 = System.nanoTime(); if (n == null) { n = stack.loadNodeAt(location); } stack.getTimeReport().addTime(System.nanoTime() - t1, "loadnode"); return n; } public ByteBuffer get(Lobstack stack, String key) throws IOException { if (key.equals(prefix)) { return null; } if (children.containsKey(key)) { NodeEntry ne = children.get(key); if (ne.node) { return stack.loadNodeAt(ne.location).get(stack, key); } else { return stack.loadAtLocation(ne.location); } } String sub = children.floorKey(key); if (sub != null) { if (key.startsWith(sub)) { NodeEntry ne = children.get(sub); if (ne.node) { return stack.loadNodeAt(ne.location).get(stack,key); } } } return null; } public Map<String, ByteBuffer> getByPrefix(Lobstack stack, String key_prefix) throws IOException { TreeMap<String, ByteBuffer> ret = new TreeMap<String, ByteBuffer>(); for(String sub : children.keySet()) { if ((sub.startsWith(key_prefix)) || (key_prefix.startsWith(sub))) { NodeEntry ne = children.get(sub); if (ne.node) { ret.putAll( stack.loadNodeAt(ne.location).getByPrefix(stack, key_prefix) ); } else { ret.put(sub, stack.loadAtLocation(ne.location)); } } } return ret; } public ByteBuffer serialize() { try { ByteArrayOutputStream b_out = new ByteArrayOutputStream(); DataOutputStream d_out = new DataOutputStream(b_out); SerialUtil.writeString(d_out, prefix); d_out.writeInt(NODE_VERSION); d_out.writeInt(children.size()); for(Map.Entry<String, NodeEntry> me : children.entrySet()) { String sub = me.getKey(); NodeEntry ne = me.getValue(); String subsub = sub.substring(prefix.length()); SerialUtil.writeString(d_out, subsub); d_out.writeBoolean(ne.node); d_out.writeLong(ne.location); d_out.writeInt(ne.min_file_number); } d_out.flush(); d_out.close(); return ByteBuffer.wrap(b_out.toByteArray()); } catch(Exception e) { throw new RuntimeException(e);} } public static LobstackNode deserialize(ByteBuffer buf) { try { ByteArrayInputStream b_in = new ByteArrayInputStream(buf.array()); DataInputStream d_in = new DataInputStream(b_in); String prefix = SerialUtil.readString(d_in); int v = d_in.readInt(); int read_version=-1; if (v < 0) { read_version=v; } int count; if (read_version==-1) { count = v; } else { count = d_in.readInt(); } TreeMap<String, NodeEntry> c = new TreeMap<String, NodeEntry>(); for(int i=0; i<count; i++) { String sub = prefix + SerialUtil.readString(d_in); NodeEntry ne = new NodeEntry(); ne.node = d_in.readBoolean(); ne.location = d_in.readLong(); if (read_version <= -2) { ne.min_file_number = d_in.readInt(); } c.put(sub, ne); } return new LobstackNode(prefix, c); } catch(Exception e) { throw new RuntimeException(e);} } public static int commonLength(Lobstack stack, String a, String b) { int max = Math.min(a.length(), b.length()); int same = 0; for(int i=0; i<max; i++) { if (a.charAt(i) == b.charAt(i)) same++; else break; } if (stack.key_step_size > 1) { same -= (same % stack.key_step_size); } return same; } }