You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2013/02/22 13:23:15 UTC

svn commit: r1449014 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/segment/ test/java/org/apache/jackrabbit/oak/plugins/segment/

Author: jukka
Date: Fri Feb 22 12:23:14 2013
New Revision: 1449014

URL: http://svn.apache.org/r1449014
Log:
OAK-632: SegmentMK: Efficient updates of flat nodes

Some initial cleanup and optimization of the HAMT code

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapBranch.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapLeaf.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapRecord.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MemoryStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MongoStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentReader.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Template.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/RecordTest.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentSizeTest.java

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapBranch.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapBranch.java?rev=1449014&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapBranch.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapBranch.java Fri Feb 22 12:23:14 2013
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.segment;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.concat;
+import static com.google.common.collect.Iterables.transform;
+import static java.lang.Integer.bitCount;
+import static org.apache.jackrabbit.oak.plugins.segment.Segment.RECORD_ID_BYTES;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+
+class MapBranch extends MapRecord {
+
+    private final int bitmap;
+
+    MapBranch(SegmentStore store, RecordId id, int size, int level, int bitmap) {
+        super(store, id, size, level);
+        checkArgument(size > BUCKETS_PER_LEVEL);
+        checkArgument(level < MAX_NUMBER_OF_LEVELS);
+        this.bitmap = bitmap;
+    }
+
+    @Override
+    RecordId getEntry(String key) {
+        checkNotNull(key);
+
+        int mask = BUCKETS_PER_LEVEL - 1;
+        int shift = level * LEVEL_BITS;
+        int index = (key.hashCode() >> shift) & mask;
+
+        int bit = 1 << index;
+        if ((bitmap & bit) != 0) {
+            int offset = getOffset()
+                    + 8 + bitCount(bitmap & (bit - 1)) * RECORD_ID_BYTES;
+            RecordId id = getSegment().readRecordId(offset);
+            return MapRecord.readMap(store, id).getEntry(key);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    Iterable<String> getKeys() {
+        return concat(transform(
+                getBuckets(),
+                new Function<RecordId, Iterable<String>>() {
+                    @Override @Nullable
+                    public Iterable<String> apply(@Nullable RecordId input) {
+                        return MapRecord.readMap(store, input).getKeys();
+                    }
+                }));
+    }
+
+    @Override
+    Iterable<Entry> getEntries() {
+        return concat(transform(
+                getBuckets(),
+                new Function<RecordId, Iterable<Entry>>() {
+                    @Override @Nullable
+                    public Iterable<Entry> apply(@Nullable RecordId input) {
+                        return MapRecord.readMap(store, input).getEntries();
+                    }
+                }));
+    }
+
+    private Iterable<RecordId> getBuckets() {
+        int n = Integer.bitCount(bitmap);
+        int p = getOffset() + 8;
+        int q = p + n * RECORD_ID_BYTES;
+        Segment segment = getSegment();
+
+        List<RecordId> buckets = Lists.newArrayListWithCapacity(n);
+        for (int o = p; o < q; o += RECORD_ID_BYTES) {
+            buckets.add(segment.readRecordId(o));
+        }
+        return buckets;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapLeaf.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapLeaf.java?rev=1449014&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapLeaf.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapLeaf.java Fri Feb 22 12:23:14 2013
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.segment;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.plugins.segment.Segment.RECORD_ID_BYTES;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+class MapLeaf extends MapRecord {
+
+    MapLeaf(SegmentStore store, RecordId id, int size, int level) {
+        super(store, id, size, level);
+        System.out.println(size + " " + level);
+        checkArgument(size != 0 || level == 0);
+        checkArgument(size <= BUCKETS_PER_LEVEL || level == MAX_NUMBER_OF_LEVELS);
+    }
+
+    @Override
+    RecordId getEntry(String key) {
+        checkNotNull(key);
+
+        if (size > 0) {
+            int hash = key.hashCode();
+            Segment segment = getSegment();
+
+            int index = 0;
+            while (index < size && getHash(segment, index) < hash) {
+                index++;
+            }
+            while (index < size && getHash(segment, index) == hash) {
+                if (key.equals(getKey(segment, index))) {
+                    return getValue(segment, index);
+                }
+                index++;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    Iterable<String> getKeys() {
+        return new Iterable<String>() {
+            @Override
+            public Iterator<String> iterator() {
+                return getKeyIterator();
+            }
+
+        };
+    }
+
+    @Override
+    Iterable<Entry> getEntries() {
+        return new Iterable<Entry>() {
+            @Override
+            public Iterator<Entry> iterator() {
+                return getEntryIterator();
+            }
+        };
+    }
+
+    //-----------------------------------------------------------< private >--
+
+    private Iterator<String> getKeyIterator() {
+        return new Iterator<String>() {
+            private final Segment segment = getSegment();
+            private int index = 0;
+            @Override
+            public boolean hasNext() {
+                return index < size;
+            }
+            @Override
+            public String next() {
+                int i = index++;
+                if (i < size) {
+                    return getKey(segment, i);
+                } else {
+                    throw new NoSuchElementException();
+                }
+            }
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    private Iterator<Entry> getEntryIterator() {
+        return new Iterator<Entry>() {
+            private final Segment segment = getSegment();
+            private int index = 0;
+            @Override
+            public boolean hasNext() {
+                return index < size;
+            }
+            @Override
+            public Entry next() {
+                final int i = index++;
+                if (i < size) {
+                    return new Entry() {
+                        @Override
+                        public String getKey() {
+                            return MapLeaf.this.getKey(segment, i);
+                        }
+                        @Override
+                        public RecordId getValue() {
+                            return MapLeaf.this.getValue(segment, i);
+                        }
+                        @Override
+                        public RecordId setValue(RecordId value) {
+                            throw new UnsupportedOperationException();
+                        }
+                    };
+                } else {
+                    throw new NoSuchElementException();
+                }
+            }
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    private int getHash(Segment segment, int index) {
+        return checkNotNull(segment).readInt(getOffset() + 4 + index * 4);
+    }
+
+    private String getKey(Segment segment, int index) {
+        int offset = getOffset() + 4 + size * 4 + index * RECORD_ID_BYTES;
+        RecordId id = checkNotNull(segment).readRecordId(offset);
+        if (!segment.getSegmentId().equals(id.getSegmentId())) {
+            // the string is stored in another segment
+            segment = store.readSegment(id.getSegmentId());
+        }
+        return segment.readString(id.getOffset());
+    }
+
+    private RecordId getValue(Segment segment, int index) {
+        int offset = getOffset()
+                + 4 + size * 4 + size * RECORD_ID_BYTES
+                + index * RECORD_ID_BYTES;
+        return checkNotNull(segment).readRecordId(offset);
+    }
+
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapRecord.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapRecord.java?rev=1449014&r1=1449013&r2=1449014&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapRecord.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MapRecord.java Fri Feb 22 12:23:14 2013
@@ -16,145 +16,123 @@
  */
 package org.apache.jackrabbit.oak.plugins.segment;
 
+import static com.google.common.base.Preconditions.checkElementIndex;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.Integer.highestOneBit;
+import static java.lang.Integer.numberOfTrailingZeros;
 
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
+abstract class MapRecord extends Record {
 
-class MapRecord extends Record {
+    /**
+     * Number of bits of the hash code to look at on each level of the trie.
+     */
+    protected static final int BITS_PER_LEVEL = 5;
+
+    /**
+     * Number of buckets at each level of the trie.
+     */
+    protected static final int BUCKETS_PER_LEVEL = 1 << BITS_PER_LEVEL; // 32
+
+    /**
+     * Maximum number of trie levels.
+     */
+    protected static final int MAX_NUMBER_OF_LEVELS =
+            (32 + BITS_PER_LEVEL - 1) / BITS_PER_LEVEL; // 7
+
+    /**
+     * Number of bits needed to indicate the current trie level.
+     */
+    protected static final int LEVEL_BITS = // 4, using nextPowerOfTwo():
+            numberOfTrailingZeros(highestOneBit(MAX_NUMBER_OF_LEVELS) << 1);
+
+    /**
+     * Number of bits used to indicate the size of a map.
+     */
+    protected static final int SIZE_BITS = 32 - LEVEL_BITS;
+
+    /**
+     * Maximum size of a map.
+     */
+    protected static final int MAX_SIZE = (1 << SIZE_BITS) - 1; // ~268e6
+
+    static MapRecord readMap(SegmentStore store, RecordId id) {
+        checkNotNull(store);
+        checkNotNull(id);
+
+        Segment segment = checkNotNull(store).readSegment(id.getSegmentId());
+        int head = segment.readInt(id.getOffset());
+        int level = head >>> SIZE_BITS;
+        int size = head & ((1 << SIZE_BITS) - 1);
+        System.out.println("R: " + size + " " + level);
+        if (size > BUCKETS_PER_LEVEL && level < MAX_NUMBER_OF_LEVELS) {
+            int bitmap = segment.readInt(id.getOffset() + 4);
+            return new MapBranch(store, id, size, level, bitmap);
+        } else {
+            return new MapLeaf(store, id, size, level);
+        }
+    }
 
     public interface Entry extends Map.Entry<String, RecordId> {    
     }
 
-    static final int LEVEL_BITS = 5;
+    protected final SegmentStore store;
 
-    MapRecord(RecordId id) {
-        super(id);
-    }
+    protected final int size;
 
-    public int size(SegmentReader reader) {
-        return reader.readInt(getRecordId(), 0);
+    protected final int level;
+
+    protected MapRecord(SegmentStore store, RecordId id, int size, int level) {
+        super(checkNotNull(id));
+        this.store = checkNotNull(store);
+        this.size = checkElementIndex(size, MAX_SIZE);
+        this.level = checkElementIndex(level, MAX_NUMBER_OF_LEVELS);
     }
 
-    public RecordId getEntry(SegmentReader reader, String key) {
-        checkNotNull(key);
-        return getEntry(reader, key, 0);
+    protected Segment getSegment() {
+        return getSegment(getRecordId().getSegmentId());
     }
 
-    private int getHash(SegmentReader reader, int index) {
-        return reader.readInt(getRecordId(), 4 + index * 4);
+    protected Segment getSegment(UUID uuid) {
+        return store.readSegment(uuid);
     }
 
-    private String getKey(SegmentReader reader, int size, int index) {
-        int offset = 4 + size * 4 + index * Segment.RECORD_ID_BYTES;
-        return reader.readString(reader.readRecordId(getRecordId(), offset));
+    protected int getOffset() {
+        return getRecordId().getOffset();
     }
 
-    private RecordId getValue(SegmentReader reader, int size, int index) {
-        int offset = 4 + size * 4 + (size + index) * Segment.RECORD_ID_BYTES;
-        return reader.readRecordId(getRecordId(), offset);
+    int size() {
+        return size;
     }
 
-    private RecordId getEntry(SegmentReader reader, String key, int level) {
-        int size = 1 << LEVEL_BITS;
-        int mask = size - 1;
-        int shift = level * LEVEL_BITS;
+    abstract RecordId getEntry(String key);
 
-        int code = key.hashCode();
-        int bucketSize = reader.readInt(getRecordId(), 0);
-        if (bucketSize == 0) {
-            return null;
-        } else if (bucketSize <= size || shift >= 32) {
-            int index = 0;
-            while (index < bucketSize && getHash(reader, index) < code) {
-                index++;
-            }
-            while (index < bucketSize && getHash(reader, index) == code) {
-                if (key.equals(getKey(reader, bucketSize, index))) {
-                    return getValue(reader, bucketSize, index);
-                }
-                index++;
-            }
-            return null;
-        } else {
-            int bucketMap = reader.readInt(getRecordId(), 4);
-            int bucketIndex = (code >> shift) & mask;
-            int bucketBit = 1 << bucketIndex;
-            if ((bucketMap & bucketBit) != 0) {
-                bucketIndex = Integer.bitCount(bucketMap & (bucketBit - 1));
-                RecordId bucketId = reader.readRecordId(getRecordId(), 8 + bucketIndex * Segment.RECORD_ID_BYTES);
-                return new MapRecord(bucketId).getEntry(reader, key, level + 1);
+    abstract Iterable<String> getKeys();
+
+    abstract Iterable<Entry> getEntries();
+
+    //------------------------------------------------------------< Object >--
+
+    @Override
+    public String toString() {
+        StringBuilder builder = null;
+        for (Entry entry : getEntries()) {
+            if (builder == null) {
+                builder = new StringBuilder("{ ");
             } else {
-                return null;
+                builder.append(", ");
             }
+            builder.append(entry.getKey());
+            builder.append("=");
+            builder.append(entry.getValue());
         }
-    }
-
-    public Iterable<Entry> getEntries(SegmentReader reader) {
-        return getEntries(reader, 0);
-    }
-
-    private Iterable<Entry> getEntries(
-            final SegmentReader reader, int level) {
-        int size = 1 << LEVEL_BITS;
-        int shift = level * LEVEL_BITS;
-
-        final int bucketSize = reader.readInt(getRecordId(), 0);
-        if (bucketSize == 0) {
-            return Collections.emptyList();
-        } else if (bucketSize <= size || shift >= 32) {
-            return new Iterable<Entry>() {
-                @Override
-                public Iterator<Entry> iterator() {
-                    return new Iterator<Entry>() {
-                        private int index = 0;
-                        @Override
-                        public boolean hasNext() {
-                            return index < bucketSize;
-                        }
-                        @Override
-                        public Entry next() {
-                            final int i = index++;
-                            return new Entry() {
-                                @Override
-                                public String getKey() {
-                                    return MapRecord.this.getKey(
-                                            reader, bucketSize, i);
-                                }
-                                @Override
-                                public RecordId getValue() {
-                                    return MapRecord.this.getValue(
-                                            reader, bucketSize, i);
-                                }
-                                @Override
-                                public RecordId setValue(RecordId value) {
-                                    throw new UnsupportedOperationException();
-                                }
-                            };
-                        }
-                        @Override
-                        public void remove() {
-                            throw new UnsupportedOperationException();
-                        }
-                    };
-                }
-            };
+        if (builder == null) {
+            return "{}";
         } else {
-            int bucketMap = reader.readInt(getRecordId(), 4);
-            int bucketCount = Integer.bitCount(bucketMap);
-            List<Iterable<Entry>> iterables =
-                    Lists.newArrayListWithCapacity(bucketCount);
-            for (int i = 0; i < bucketCount; i++) {
-                RecordId bucketId = reader.readRecordId(
-                        getRecordId(), 8 + i * Segment.RECORD_ID_BYTES);
-                iterables.add(new MapRecord(bucketId).getEntries(reader, level + 1));
-            }
-            return Iterables.concat(iterables);
+            builder.append(" }");
+            return builder.toString();
         }
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MemoryStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MemoryStore.java?rev=1449014&r1=1449013&r2=1449014&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MemoryStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MemoryStore.java Fri Feb 22 12:23:14 2013
@@ -80,7 +80,7 @@ public class MemoryStore implements Segm
             UUID segmentId, byte[] data, int offset, int length) {
         byte[] segment = new byte[length];
         System.arraycopy(data, offset, segment, 0, length);
-        createSegment(new Segment(segmentId, segment, new UUID[0]));
+        createSegment(new Segment(this, segmentId, segment, new UUID[0]));
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MongoStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MongoStore.java?rev=1449014&r1=1449013&r2=1449014&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MongoStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/MongoStore.java Fri Feb 22 12:23:14 2013
@@ -121,7 +121,7 @@ public class MongoStore implements Segme
         for (int i = 0; i < uuids.length; i++) {
             uuids[i] = UUID.fromString(list.get(i).toString());
         }
-        return new Segment(segmentId, data, uuids);
+        return new Segment(this, segmentId, data, uuids);
     }
 
     private void insertSegment(UUID segmentId, byte[] data, UUID[] uuids) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java?rev=1449014&r1=1449013&r2=1449014&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java Fri Feb 22 12:23:14 2013
@@ -81,6 +81,8 @@ class Segment {
                 }
             };
 
+    private final SegmentStore store;
+
     private final UUID uuid;
 
     private final byte[] data;
@@ -90,7 +92,8 @@ class Segment {
     private final OffsetCache<String> strings = new OffsetCache<String>() {
         @Override
         protected String load(int offset) {
-            return null;
+            return new SegmentReader(store).readString(
+                    new RecordId(uuid, offset));
         }
     };
 
@@ -101,7 +104,8 @@ class Segment {
         }
     };
 
-    Segment(UUID uuid, byte[] data, UUID[] uuids) {
+    Segment(SegmentStore store, UUID uuid, byte[] data, UUID[] uuids) {
+        this.store = store;
         this.uuid = uuid;
         this.data = data;
         this.uuids = uuids;
@@ -168,4 +172,8 @@ class Segment {
         return ByteBuffer.wrap(data).getLong(pos);
     }
 
+    String readString(int offset) {
+        return strings.get(offset);
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentReader.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentReader.java?rev=1449014&r1=1449013&r2=1449014&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentReader.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentReader.java Fri Feb 22 12:23:14 2013
@@ -264,4 +264,8 @@ public class SegmentReader {
         return new BlockRecord(recordId, size);
     }
 
+    SegmentStore getStore() {
+        return store;
+    }
+
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java?rev=1449014&r1=1449013&r2=1449014&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java Fri Feb 22 12:23:14 2013
@@ -45,6 +45,7 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.io.ByteStreams;
@@ -102,7 +103,7 @@ public class SegmentWriter {
             }
 
             store.createSegment(new Segment(
-                    uuid, data, uuids.toArray(new UUID[uuids.size()])));
+                    store, uuid, data, uuids.toArray(new UUID[uuids.size()])));
 
             uuid = UUID.randomUUID();
             length = 0;
@@ -230,29 +231,28 @@ public class SegmentWriter {
         }
     }
 
-    private synchronized BucketInfo writeMapBucket(
+    private synchronized MapRecord writeMapBucket(
             RecordId baseId, List<MapEntry> entries, int level) {
-        int size = 1 << MapRecord.LEVEL_BITS;
-        int mask = size - 1;
+        int mask = MapRecord.BUCKETS_PER_LEVEL - 1;
         int shift = level * MapRecord.LEVEL_BITS;
 
         if (entries.isEmpty()) {
             if (baseId != null) {
-                return new BucketInfo(baseId, new MapRecord(baseId).size(reader));
+                return MapRecord.readMap(store, baseId);
             } else if (level == 0) {
                 RecordId id = prepare(4);
                 writeInt(0);
-                return new BucketInfo(id, 0);
+                return new MapLeaf(store, id, 0, 0);
             } else {
-                return new BucketInfo(null, 0);
+                return null;
             }
         } else if (baseId != null) {
             // FIXME: messy code with lots of duplication
-            MapRecord base = new MapRecord(baseId);
-            int baseSize = base.size(reader);
-            if (baseSize <= size) {
+            MapRecord base = MapRecord.readMap(store, baseId);
+            int baseSize = base.size();
+            if (baseSize <= MapRecord.BUCKETS_PER_LEVEL) {
                 Map<String, RecordId> update = Maps.newHashMap();
-                for (MapRecord.Entry entry : base.getEntries(reader)) {
+                for (MapRecord.Entry entry : base.getEntries()) {
                     update.put(entry.getKey(), entry.getValue());
                 }
                 for (MapEntry entry : entries) {
@@ -268,7 +268,7 @@ public class SegmentWriter {
                 }
                 return writeMapBucket(null, entries, level);
             } else {
-                List<MapEntry>[] buckets = new List[size];
+                List<MapEntry>[] buckets = new List[MapRecord.BUCKETS_PER_LEVEL];
                 for (MapEntry entry : entries) {
                     int bucketIndex = (entry.hashCode >> shift) & mask;
                     if (buckets[bucketIndex] == null) {
@@ -280,7 +280,7 @@ public class SegmentWriter {
                 int baseMap = reader.readInt(baseId, 4);
 
                 int newSize = 0;
-                RecordId[] bucketIds = new RecordId[size];
+                RecordId[] bucketIds = new RecordId[MapRecord.BUCKETS_PER_LEVEL];
                 for (int i = 0; i < buckets.length; i++) {
                     int bucketBit = 1 << i;
                     RecordId baseBucketId = null;
@@ -290,14 +290,14 @@ public class SegmentWriter {
                                 baseId, 8 + index * Segment.RECORD_ID_BYTES);
                     }
                     if (buckets[i] != null) {
-                        BucketInfo info = writeMapBucket(
+                        MapRecord map = writeMapBucket(
                                 baseBucketId, buckets[i], level + 1);
-                        bucketIds[i] = info.id;
-                        newSize += info.size;
+                        bucketIds[i] = map.getRecordId();
+                        newSize += map.size();
                     } else {
                         bucketIds[i] = baseBucketId;
                         if (baseBucketId != null) {
-                            newSize += new MapRecord(baseBucketId).size(reader);
+                            newSize += MapRecord.readMap(store, baseBucketId).size();
                         }
                     }
                 }
@@ -312,14 +312,14 @@ public class SegmentWriter {
                 }
 
                 RecordId bucketId = prepare(12, ids);
-                writeInt(newSize);
+                writeInt((level << MapRecord.SIZE_BITS) | newSize);
                 writeInt(bucketMap);
                 for (RecordId id : ids) {
                     writeRecordId(id);
                 }
-                return new BucketInfo(bucketId, newSize);
+                return new MapBranch(store, bucketId, newSize, level, bucketMap);
             }
-        } else if (entries.size() <= size) {
+        } else if (entries.size() <= MapRecord.BUCKETS_PER_LEVEL) {
             Collections.sort(entries);
 
             List<RecordId> ids = Lists.newArrayList();
@@ -329,7 +329,9 @@ public class SegmentWriter {
             }
 
             RecordId bucketId = prepare(4 + entries.size() * 4, ids);
-            writeInt(entries.size());
+            // System.out.println("W: " + entries.size() + " " + level);
+            Preconditions.checkState(entries.size() > 0 || level == 0);
+            writeInt((level << MapRecord.SIZE_BITS) | entries.size());
             for (MapEntry entry : entries) {
                 writeInt(entry.hashCode);
             }
@@ -339,9 +341,9 @@ public class SegmentWriter {
             for (MapEntry entry : entries) {
                 writeRecordId(entry.value);
             }
-            return new BucketInfo(bucketId, entries.size());
+            return new MapLeaf(store, bucketId, entries.size(), level);
         } else {
-            List<MapEntry>[] buckets = new List[size];
+            List<MapEntry>[] buckets = new List[MapRecord.BUCKETS_PER_LEVEL];
             for (MapEntry entry : entries) {
                 int bucketIndex = (entry.hashCode >> shift) & mask;
                 if (buckets[bucketIndex] == null) {
@@ -354,18 +356,18 @@ public class SegmentWriter {
             int bucketMap = 0;
             for (int i = 0; i < buckets.length; i++) {
                 if (buckets[i] != null) {
-                    bucketIds.add(writeMapBucket(null, buckets[i], level + 1).id);
+                    bucketIds.add(writeMapBucket(null, buckets[i], level + 1).getRecordId());
                     bucketMap |= 1 << i;
                 }
             }
 
             RecordId bucketId = prepare(8, bucketIds);
-            writeInt(entries.size());
+            writeInt((level << MapRecord.SIZE_BITS) | entries.size());
             writeInt(bucketMap);
             for (RecordId id : bucketIds) {
                 writeRecordId(id);
             }
-            return new BucketInfo(bucketId, entries.size());
+            return new MapBranch(store, bucketId, entries.size(), level, bucketMap);
         }
     }
 
@@ -427,7 +429,7 @@ public class SegmentWriter {
         if (base != null) {
             baseId = base.getRecordId();
         }
-        return new MapRecord(writeMapBucket(baseId, entries, 0).id);
+        return writeMapBucket(baseId, entries, 0);
     }
 
     /**

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Template.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Template.java?rev=1449014&r1=1449013&r2=1449014&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Template.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Template.java Fri Feb 22 12:23:14 2013
@@ -247,7 +247,7 @@ class Template {
         } else if (hasManyChildNodes()) {
             RecordId childNodesId =
                     reader.readRecordId(recordId, Segment.RECORD_ID_BYTES);
-            return new MapRecord(childNodesId).size(reader);
+            return MapRecord.readMap(reader.getStore(), childNodesId).size();
         } else {
             return 1;
         }
@@ -255,7 +255,8 @@ class Template {
 
     MapRecord getChildNodeMap(SegmentReader reader, RecordId recordId) {
         checkState(hasManyChildNodes());
-        return new MapRecord(
+        return MapRecord.readMap(
+                reader.getStore(),
                 reader.readRecordId(recordId, Segment.RECORD_ID_BYTES));
     }
 
@@ -265,7 +266,7 @@ class Template {
             return false;
         } else if (hasManyChildNodes()) {
             MapRecord map = getChildNodeMap(reader, recordId);
-            return map.getEntry(reader, name) != null;
+            return map.getEntry(name) != null;
         } else {
             return name.equals(childName);
         }
@@ -277,7 +278,7 @@ class Template {
             return null;
         } else if (hasManyChildNodes()) {
             RecordId childNodeId =
-                    getChildNodeMap(reader, recordId).getEntry(reader, name);
+                    getChildNodeMap(reader, recordId).getEntry(name);
             if (childNodeId != null) {
                 return new SegmentNodeState(reader, childNodeId);
             } else {
@@ -297,14 +298,7 @@ class Template {
         if (hasNoChildNodes()) {
             return Collections.emptyList();
         } else if (hasManyChildNodes()) {
-            return Iterables.transform(
-                    getChildNodeMap(reader, recordId).getEntries(reader),
-                    new Function<MapRecord.Entry, String>() {
-                        @Override @Nullable
-                        public String apply(@Nullable Entry input) {
-                            return input.getKey();
-                        }
-                    });
+            return getChildNodeMap(reader, recordId).getKeys();
         } else {
             return Collections.singletonList(childName);
         }
@@ -316,7 +310,7 @@ class Template {
             return Collections.emptyList();
         } else if (hasManyChildNodes()) {
             return Iterables.transform(
-                    getChildNodeMap(reader, recordId).getEntries(reader),
+                    getChildNodeMap(reader, recordId).getEntries(),
                     new Function<MapRecord.Entry, ChildNodeEntry>() {
                         @Override @Nullable
                         public ChildNodeEntry apply(@Nullable Entry input) {

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/RecordTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/RecordTest.java?rev=1449014&r1=1449013&r2=1449014&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/RecordTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/RecordTest.java Fri Feb 22 12:23:14 2013
@@ -187,54 +187,55 @@ public class RecordTest {
         writer.flush();
         Iterator<MapRecord.Entry> iterator;
 
-        assertEquals(0, zero.size(reader));
-        assertNull(zero.getEntry(reader, "one"));
-        iterator = zero.getEntries(reader).iterator();
+        assertEquals(0, zero.size());
+        assertNull(zero.getEntry("one"));
+        iterator = zero.getEntries().iterator();
         assertFalse(iterator.hasNext());
 
-        assertEquals(1, one.size(reader));
-        assertEquals(blockId, one.getEntry(reader, "one"));
-        assertNull(one.getEntry(reader, "two"));
-        iterator = one.getEntries(reader).iterator();
+        assertEquals(1, one.size());
+        assertEquals(blockId, one.getEntry("one"));
+        assertNull(one.getEntry("two"));
+        iterator = one.getEntries().iterator();
         assertTrue(iterator.hasNext());
         assertEquals("one", iterator.next().getKey());
         assertFalse(iterator.hasNext());
 
-        assertEquals(2, two.size(reader));
-        assertEquals(blockId, two.getEntry(reader, "one"));
-        assertEquals(blockId, two.getEntry(reader, "two"));
-        assertNull(two.getEntry(reader, "three"));
-        iterator = two.getEntries(reader).iterator();
+        assertEquals(2, two.size());
+        assertEquals(blockId, two.getEntry("one"));
+        assertEquals(blockId, two.getEntry("two"));
+        assertNull(two.getEntry("three"));
+        iterator = two.getEntries().iterator();
         assertTrue(iterator.hasNext());
         iterator.next();
         assertTrue(iterator.hasNext());
         iterator.next();
         assertFalse(iterator.hasNext());
 
-        assertEquals(1000, many.size(reader));
-        iterator = many.getEntries(reader).iterator();
+        assertEquals(1000, many.size());
+        iterator = many.getEntries().iterator();
+        System.out.println(MapRecord.readMap(store, many.getRecordId()));
         for (int i = 0; i < 1000; i++) {
             assertTrue(iterator.hasNext());
             assertEquals(blockId, iterator.next().getValue());
-            assertEquals(blockId, many.getEntry(reader, "key" + i));
+            assertEquals(blockId, many.getEntry("key" + i));
         }
         assertFalse(iterator.hasNext());
-        assertNull(many.getEntry(reader, "foo"));
+        assertNull(many.getEntry("foo"));
 
         Map<String, RecordId> changes = Maps.newHashMap();
         changes.put("key0", null);
         changes.put("key1000", blockId);
         MapRecord modified = writer.writeMap(many, changes);
         writer.flush();
-        assertEquals(1000, modified.size(reader));
-        iterator = modified.getEntries(reader).iterator();
+        assertEquals(1000, modified.size());
+        iterator = modified.getEntries().iterator();
         for (int i = 1; i <= 1000; i++) {
             assertTrue(iterator.hasNext());
             assertEquals(blockId, iterator.next().getValue());
-            assertEquals(blockId, modified.getEntry(reader, "key" + i));
+            assertEquals(blockId, modified.getEntry("key" + i));
         }
         assertFalse(iterator.hasNext());
-        assertNull(many.getEntry(reader, "foo"));
+        assertNull(many.getEntry("foo"));
     }
 
     @Test

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentSizeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentSizeTest.java?rev=1449014&r1=1449013&r2=1449014&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentSizeTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentSizeTest.java Fri Feb 22 12:23:14 2013
@@ -156,7 +156,7 @@ public class SegmentSizeTest {
         SegmentNodeState state = writer.writeNode(builder.getNodeState());
         writer.flush();
         Segment segment = store.readSegment(state.getRecordId().getSegmentId());
-        assertEquals(28588, segment.getData().length);
+        assertEquals(26788, segment.getData().length);
 
         builder = state.builder();
         builder.child("child1000");