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/10/25 16:11:58 UTC

svn commit: r1535743 - 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 Oct 25 14:11:58 2013
New Revision: 1535743

URL: http://svn.apache.org/r1535743
Log:
OAK-593: Segment-based MK

Simplify segment size and record position handling

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/AbstractStore.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentSizeTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/AbstractStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/AbstractStore.java?rev=1535743&r1=1535742&r2=1535743&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/AbstractStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/AbstractStore.java Fri Oct 25 14:11:58 2013
@@ -25,7 +25,6 @@ import java.util.concurrent.Callable;
 import org.apache.jackrabbit.oak.cache.CacheLIRS;
 
 import com.google.common.cache.Cache;
-import com.google.common.cache.Weigher;
 
 public abstract class AbstractStore implements SegmentStore {
 
@@ -43,12 +42,7 @@ public abstract class AbstractStore impl
 
     protected AbstractStore(int cacheSize) {
         this.segments = CacheLIRS.newBuilder()
-                .weigher(new Weigher<UUID, Segment>() {
-                    @Override
-                    public int weigh(UUID key, Segment value) {
-                        return value.size();
-                    }
-                })
+                .weigher(Segment.WEIGHER)
                 .maximumWeight(cacheSize)
                 .build();
     }

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=1535743&r1=1535742&r2=1535743&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 Oct 25 14:11:58 2013
@@ -1,360 +1,354 @@
-/*
- * 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.Objects.equal;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkPositionIndexes;
-import static org.apache.jackrabbit.oak.plugins.segment.SegmentWriter.BLOCK_SIZE;
-
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.Callable;
-
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
-
-import com.google.common.base.Charsets;
-import com.google.common.cache.Weigher;
-
-public class Segment {
-
-    /**
-     * Number of bytes used for storing a record identifier. One byte
-     * is used for identifying the segment and two for the record offset
-     * within that segment.
-     */
-    static final int RECORD_ID_BYTES = 1 + 2;
-
-    /**
-     * The limit on segment references within one segment. Since record
-     * identifiers use one byte to indicate the referenced segment, a single
-     * segment can hold references to up to 256 segments.
-     */
-    static final int SEGMENT_REFERENCE_LIMIT = 1 << 8; // 256
-
-    /**
-     * The number of bytes (or bits of address space) to use for the
-     * alignment boundary of segment records.
-     */
-    static final int RECORD_ALIGN_BITS = 2;
-    static final int RECORD_ALIGN_BYTES = 1 << RECORD_ALIGN_BITS; // 4
-
-    /**
-     * Maximum segment size. Record identifiers are stored as three-byte
-     * sequences with the first byte indicating the segment and the next
-     * two the offset within that segment. Since all records are aligned
-     * at four-byte boundaries, the two bytes can address up to 256kB of
-     * record data.
-     */
-    static final int MAX_SEGMENT_SIZE = 1 << (16 + RECORD_ALIGN_BITS); // 256kB
-
-    /**
-     * The size limit for small values. The variable length of small values
-     * is encoded as a single byte with the high bit as zero, which gives us
-     * seven bits for encoding the length of the value.
-     */
-    static final int SMALL_LIMIT = 1 << 7;
-
-    /**
-     * The size limit for medium values. The variable length of medium values
-     * is encoded as two bytes with the highest bits of the first byte set to
-     * one and zero, which gives us 14 bits for encoding the length of the
-     * value. And since small values are never stored as medium ones, we can
-     * extend the size range to cover that many longer values.
-     */
-    static final int MEDIUM_LIMIT = (1 << (16 - 2)) + SMALL_LIMIT;
-
-    static final Weigher<UUID, Segment> WEIGHER =
-            new Weigher<UUID, Segment>() {
-                @Override
-                public int weigh(UUID key, Segment value) {
-                    return value.size();
-                }
-            };
-
-    final SegmentStore store; // TODO: should be private
-
-    private final UUID uuid;
-
-    private final ByteBuffer data;
-
-    private final List<UUID> uuids;
-
-    public Segment(
-            SegmentStore store, UUID uuid, ByteBuffer data, List<UUID> uuids) {
-        this.store = checkNotNull(store);
-        this.uuid = checkNotNull(uuid);
-        this.data = checkNotNull(data);
-        this.uuids = checkNotNull(uuids);
-    }
-
-    /**
-     * Maps the given record offset to the respective position within the
-     * internal {@link #data} array. The validity of a record with the given
-     * length at the given offset is also verified.
-     *
-     * @param offset record offset
-     * @param length record length
-     * @return position within the data array
-     */
-    private int pos(int offset, int length) {
-        int pos = offset - (MAX_SEGMENT_SIZE - size());
-        checkPositionIndexes(pos, pos + length, size());
-        return data.position() + pos;
-    }
-
-    public UUID getSegmentId() {
-        return uuid;
-    }
-
-    public ByteBuffer getData() {
-        return data;
-    }
-
-    public int size() {
-        return data.remaining();
-    }
-
-    byte readByte(int offset) {
-        return data.get(pos(offset, 1));
-    }
-
-    /**
-     * Returns the identified segment.
-     *
-     * @param uuid segment identifier
-     * @return identified segment
-     */
-    Segment getSegment(UUID uuid) {
-        if (equal(uuid, this.uuid)) {
-            return this; // optimization for the common case (OAK-1031)
-        } else {
-            return store.readSegment(uuid);
-        }
-    }
-
-    /**
-     * Returns the segment that contains the identified record.
-     *
-     * @param id record identifier
-     * @return segment that contains the identified record
-     */
-    Segment getSegment(RecordId id) {
-        return getSegment(checkNotNull(id).getSegmentId());
-    }
-
-
-    /**
-     * Reads the given number of bytes starting from the given position
-     * in this segment.
-     *
-     * @param position position within segment
-     * @param buffer target buffer
-     * @param offset offset within target buffer
-     * @param length number of bytes to read
-     */
-     void readBytes(int position, byte[] buffer, int offset, int length) {
-        checkNotNull(buffer);
-        checkPositionIndexes(offset, offset + length, buffer.length);
-        ByteBuffer d = data.duplicate();
-        d.position(pos(position, length));
-        d.get(buffer, offset, length);
-    }
-
-    RecordId readRecordId(int offset) {
-        int pos = pos(offset, RECORD_ID_BYTES);
-        return internalReadRecordId(pos);
-    }
-
-    private RecordId internalReadRecordId(int pos) {
-        return new RecordId(
-                uuids.get(data.get(pos) & 0xff),
-                (data.get(pos + 1) & 0xff) << (8 + Segment.RECORD_ALIGN_BITS)
-                | (data.get(pos + 2) & 0xff) << Segment.RECORD_ALIGN_BITS);
-    }
-
-    int readInt(int offset) {
-        int pos = pos(offset, 4);
-        return (data.get(pos) & 0xff) << 24
-                | (data.get(pos + 1) & 0xff) << 16
-                | (data.get(pos + 2) & 0xff) << 8
-                | (data.get(pos + 3) & 0xff);
-    }
-
-    String readString(final RecordId id) {
-        return store.getRecord(id, new Callable<String>() {
-            @Override
-            public String call() {
-                return getSegment(id).readString(id.getOffset());
-            }
-        });
-    }
-
-    String readString(int offset) {
-        int pos = pos(offset, 1);
-        long length = internalReadLength(pos);
-        if (length < SMALL_LIMIT) {
-            byte[] bytes = new byte[(int) length];
-            ByteBuffer buffer = data.duplicate();
-            buffer.position(pos + 1);
-            buffer.get(bytes);
-            return new String(bytes, Charsets.UTF_8);
-        } else if (length < MEDIUM_LIMIT) {
-            byte[] bytes = new byte[(int) length];
-            ByteBuffer buffer = data.duplicate();
-            buffer.position(pos + 2);
-            buffer.get(bytes);
-            return new String(bytes, Charsets.UTF_8);
-        } else if (length < Integer.MAX_VALUE) {
-            int size = (int) ((length + BLOCK_SIZE - 1) / BLOCK_SIZE);
-            ListRecord list =
-                    new ListRecord(this, internalReadRecordId(pos + 8), size);
-            SegmentStream stream = new SegmentStream(
-                    store, new RecordId(uuid, offset), list, length);
-            try {
-                return stream.getString();
-            } finally {
-                stream.close();
-            }
-        } else {
-            throw new IllegalStateException("String is too long: " + length);
-        }
-    }
-
-    MapRecord readMap(RecordId id) {
-        return new MapRecord(this, id);
-    }
-
-    Template readTemplate(final RecordId id) {
-        return store.getRecord(id, new Callable<Template>() {
-            @Override
-            public Template call() {
-                return getSegment(id).readTemplate(id.getOffset());
-            }
-        });
-    }
-
-    Template readTemplate(int offset) {
-        int head = readInt(offset);
-        boolean hasPrimaryType = (head & (1 << 31)) != 0;
-        boolean hasMixinTypes = (head & (1 << 30)) != 0;
-        boolean zeroChildNodes = (head & (1 << 29)) != 0;
-        boolean manyChildNodes = (head & (1 << 28)) != 0;
-        int mixinCount = (head >> 18) & ((1 << 10) - 1);
-        int propertyCount = head & ((1 << 18) - 1);
-        offset += 4;
-
-        PropertyState primaryType = null;
-        if (hasPrimaryType) {
-            RecordId primaryId = readRecordId(offset);
-            primaryType = PropertyStates.createProperty(
-                    "jcr:primaryType", readString(primaryId), Type.NAME);
-            offset += Segment.RECORD_ID_BYTES;
-        }
-
-        PropertyState mixinTypes = null;
-        if (hasMixinTypes) {
-            String[] mixins = new String[mixinCount];
-            for (int i = 0; i < mixins.length; i++) {
-                RecordId mixinId = readRecordId(offset);
-                mixins[i] =  readString(mixinId);
-                offset += Segment.RECORD_ID_BYTES;
-            }
-            mixinTypes = PropertyStates.createProperty(
-                    "jcr:mixinTypes", Arrays.asList(mixins), Type.NAMES);
-        }
-
-        String childName = Template.ZERO_CHILD_NODES;
-        if (manyChildNodes) {
-            childName = Template.MANY_CHILD_NODES;
-        } else if (!zeroChildNodes) {
-            RecordId childNameId = readRecordId(offset);
-            childName = readString(childNameId);
-            offset += Segment.RECORD_ID_BYTES;
-        }
-
-        PropertyTemplate[] properties =
-                new PropertyTemplate[propertyCount];
-        for (int i = 0; i < properties.length; i++) {
-            RecordId propertyNameId = readRecordId(offset);
-            offset += Segment.RECORD_ID_BYTES;
-            byte type = readByte(offset++);
-            properties[i] = new PropertyTemplate(
-                    i, readString(propertyNameId),
-                    Type.fromTag(Math.abs(type), type < 0));
-        }
-
-        return new Template(
-                primaryType, mixinTypes, properties, childName);
-    }
-
-    long readLength(RecordId id) {
-        return getSegment(id).readLength(id.getOffset());
-    }
-
-    long readLength(int offset) {
-        return internalReadLength(pos(offset, 1));
-    }
-
-    private long internalReadLength(int pos) {
-        int length = data.get(pos++) & 0xff;
-        if ((length & 0x80) == 0) {
-            return length;
-        } else if ((length & 0x40) == 0) {
-            return ((length & 0x3f) << 8
-                    | data.get(pos++) & 0xff)
-                    + SMALL_LIMIT;
-        } else {
-            return (((long) length & 0x3f) << 56
-                    | ((long) (data.get(pos++) & 0xff)) << 48
-                    | ((long) (data.get(pos++) & 0xff)) << 40
-                    | ((long) (data.get(pos++) & 0xff)) << 32
-                    | ((long) (data.get(pos++) & 0xff)) << 24
-                    | ((long) (data.get(pos++) & 0xff)) << 16
-                    | ((long) (data.get(pos++) & 0xff)) << 8
-                    | ((long) (data.get(pos++) & 0xff)))
-                    + MEDIUM_LIMIT;
-        }
-    }
-
-    SegmentStream readStream(int offset) {
-        RecordId id = new RecordId(uuid, offset);
-        int pos = pos(offset, 1);
-        long length = internalReadLength(pos);
-        if (length < Segment.MEDIUM_LIMIT) {
-            byte[] inline = new byte[(int) length];
-            ByteBuffer buffer = data.duplicate();
-            if (length < Segment.SMALL_LIMIT) {
-                buffer.position(pos + 1);
-            } else {
-                buffer.position(pos + 2);
-            }
-            buffer.get(inline);
-            return new SegmentStream(id, inline);
-        } else {
-            int size = (int) ((length + BLOCK_SIZE - 1) / BLOCK_SIZE);
-            ListRecord list =
-                    new ListRecord(this, internalReadRecordId(pos + 8), size);
-            return new SegmentStream(store, id, list, length);
-        }
-    }
-
-}
+/*
+ * 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.Objects.equal;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkPositionIndexes;
+import static com.google.common.base.Preconditions.checkState;
+import static org.apache.jackrabbit.oak.plugins.segment.SegmentWriter.BLOCK_SIZE;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+
+import com.google.common.base.Charsets;
+import com.google.common.cache.Weigher;
+
+public class Segment {
+
+    /**
+     * Number of bytes used for storing a record identifier. One byte
+     * is used for identifying the segment and two for the record offset
+     * within that segment.
+     */
+    static final int RECORD_ID_BYTES = 1 + 2;
+
+    /**
+     * The limit on segment references within one segment. Since record
+     * identifiers use one byte to indicate the referenced segment, a single
+     * segment can hold references to up to 256 segments.
+     */
+    static final int SEGMENT_REFERENCE_LIMIT = 1 << 8; // 256
+
+    /**
+     * The number of bytes (or bits of address space) to use for the
+     * alignment boundary of segment records.
+     */
+    static final int RECORD_ALIGN_BITS = 2;
+    static final int RECORD_ALIGN_BYTES = 1 << RECORD_ALIGN_BITS; // 4
+
+    /**
+     * Maximum segment size. Record identifiers are stored as three-byte
+     * sequences with the first byte indicating the segment and the next
+     * two the offset within that segment. Since all records are aligned
+     * at four-byte boundaries, the two bytes can address up to 256kB of
+     * record data.
+     */
+    static final int MAX_SEGMENT_SIZE = 1 << (16 + RECORD_ALIGN_BITS); // 256kB
+
+    /**
+     * The size limit for small values. The variable length of small values
+     * is encoded as a single byte with the high bit as zero, which gives us
+     * seven bits for encoding the length of the value.
+     */
+    static final int SMALL_LIMIT = 1 << 7;
+
+    /**
+     * The size limit for medium values. The variable length of medium values
+     * is encoded as two bytes with the highest bits of the first byte set to
+     * one and zero, which gives us 14 bits for encoding the length of the
+     * value. And since small values are never stored as medium ones, we can
+     * extend the size range to cover that many longer values.
+     */
+    static final int MEDIUM_LIMIT = (1 << (16 - 2)) + SMALL_LIMIT;
+
+    static final Weigher<UUID, Segment> WEIGHER =
+            new Weigher<UUID, Segment>() {
+                @Override
+                public int weigh(UUID key, Segment value) {
+                    return value.data.remaining();
+                }
+            };
+
+    final SegmentStore store; // TODO: should be private
+
+    private final UUID uuid;
+
+    private final ByteBuffer data;
+
+    private final List<UUID> uuids;
+
+    public Segment(
+            SegmentStore store, UUID uuid, ByteBuffer data, List<UUID> uuids) {
+        this.store = checkNotNull(store);
+        this.uuid = checkNotNull(uuid);
+        this.data = checkNotNull(data);
+        this.uuids = checkNotNull(uuids);
+    }
+
+    /**
+     * Maps the given record offset to the respective position within the
+     * internal {@link #data} array. The validity of a record with the given
+     * length at the given offset is also verified.
+     *
+     * @param offset record offset
+     * @param length record length
+     * @return position within the data array
+     */
+    private int pos(int offset, int length) {
+        checkPositionIndexes(offset, offset + length, MAX_SEGMENT_SIZE);
+        int pos = data.limit() - MAX_SEGMENT_SIZE + offset;
+        checkState(pos >= data.position());
+        return pos;
+    }
+
+    public UUID getSegmentId() {
+        return uuid;
+    }
+
+    byte readByte(int offset) {
+        return data.get(pos(offset, 1));
+    }
+
+    /**
+     * Returns the identified segment.
+     *
+     * @param uuid segment identifier
+     * @return identified segment
+     */
+    Segment getSegment(UUID uuid) {
+        if (equal(uuid, this.uuid)) {
+            return this; // optimization for the common case (OAK-1031)
+        } else {
+            return store.readSegment(uuid);
+        }
+    }
+
+    /**
+     * Returns the segment that contains the identified record.
+     *
+     * @param id record identifier
+     * @return segment that contains the identified record
+     */
+    Segment getSegment(RecordId id) {
+        return getSegment(checkNotNull(id).getSegmentId());
+    }
+
+
+    /**
+     * Reads the given number of bytes starting from the given position
+     * in this segment.
+     *
+     * @param position position within segment
+     * @param buffer target buffer
+     * @param offset offset within target buffer
+     * @param length number of bytes to read
+     */
+     void readBytes(int position, byte[] buffer, int offset, int length) {
+        checkNotNull(buffer);
+        checkPositionIndexes(offset, offset + length, buffer.length);
+        ByteBuffer d = data.duplicate();
+        d.position(pos(position, length));
+        d.get(buffer, offset, length);
+    }
+
+    RecordId readRecordId(int offset) {
+        int pos = pos(offset, RECORD_ID_BYTES);
+        return internalReadRecordId(pos);
+    }
+
+    private RecordId internalReadRecordId(int pos) {
+        return new RecordId(
+                uuids.get(data.get(pos) & 0xff),
+                (data.get(pos + 1) & 0xff) << (8 + Segment.RECORD_ALIGN_BITS)
+                | (data.get(pos + 2) & 0xff) << Segment.RECORD_ALIGN_BITS);
+    }
+
+    int readInt(int offset) {
+        int pos = pos(offset, 4);
+        return (data.get(pos) & 0xff) << 24
+                | (data.get(pos + 1) & 0xff) << 16
+                | (data.get(pos + 2) & 0xff) << 8
+                | (data.get(pos + 3) & 0xff);
+    }
+
+    String readString(final RecordId id) {
+        return store.getRecord(id, new Callable<String>() {
+            @Override
+            public String call() {
+                return getSegment(id).readString(id.getOffset());
+            }
+        });
+    }
+
+    String readString(int offset) {
+        int pos = pos(offset, 1);
+        long length = internalReadLength(pos);
+        if (length < SMALL_LIMIT) {
+            byte[] bytes = new byte[(int) length];
+            ByteBuffer buffer = data.duplicate();
+            buffer.position(pos + 1);
+            buffer.get(bytes);
+            return new String(bytes, Charsets.UTF_8);
+        } else if (length < MEDIUM_LIMIT) {
+            byte[] bytes = new byte[(int) length];
+            ByteBuffer buffer = data.duplicate();
+            buffer.position(pos + 2);
+            buffer.get(bytes);
+            return new String(bytes, Charsets.UTF_8);
+        } else if (length < Integer.MAX_VALUE) {
+            int size = (int) ((length + BLOCK_SIZE - 1) / BLOCK_SIZE);
+            ListRecord list =
+                    new ListRecord(this, internalReadRecordId(pos + 8), size);
+            SegmentStream stream = new SegmentStream(
+                    store, new RecordId(uuid, offset), list, length);
+            try {
+                return stream.getString();
+            } finally {
+                stream.close();
+            }
+        } else {
+            throw new IllegalStateException("String is too long: " + length);
+        }
+    }
+
+    MapRecord readMap(RecordId id) {
+        return new MapRecord(this, id);
+    }
+
+    Template readTemplate(final RecordId id) {
+        return store.getRecord(id, new Callable<Template>() {
+            @Override
+            public Template call() {
+                return getSegment(id).readTemplate(id.getOffset());
+            }
+        });
+    }
+
+    Template readTemplate(int offset) {
+        int head = readInt(offset);
+        boolean hasPrimaryType = (head & (1 << 31)) != 0;
+        boolean hasMixinTypes = (head & (1 << 30)) != 0;
+        boolean zeroChildNodes = (head & (1 << 29)) != 0;
+        boolean manyChildNodes = (head & (1 << 28)) != 0;
+        int mixinCount = (head >> 18) & ((1 << 10) - 1);
+        int propertyCount = head & ((1 << 18) - 1);
+        offset += 4;
+
+        PropertyState primaryType = null;
+        if (hasPrimaryType) {
+            RecordId primaryId = readRecordId(offset);
+            primaryType = PropertyStates.createProperty(
+                    "jcr:primaryType", readString(primaryId), Type.NAME);
+            offset += Segment.RECORD_ID_BYTES;
+        }
+
+        PropertyState mixinTypes = null;
+        if (hasMixinTypes) {
+            String[] mixins = new String[mixinCount];
+            for (int i = 0; i < mixins.length; i++) {
+                RecordId mixinId = readRecordId(offset);
+                mixins[i] =  readString(mixinId);
+                offset += Segment.RECORD_ID_BYTES;
+            }
+            mixinTypes = PropertyStates.createProperty(
+                    "jcr:mixinTypes", Arrays.asList(mixins), Type.NAMES);
+        }
+
+        String childName = Template.ZERO_CHILD_NODES;
+        if (manyChildNodes) {
+            childName = Template.MANY_CHILD_NODES;
+        } else if (!zeroChildNodes) {
+            RecordId childNameId = readRecordId(offset);
+            childName = readString(childNameId);
+            offset += Segment.RECORD_ID_BYTES;
+        }
+
+        PropertyTemplate[] properties =
+                new PropertyTemplate[propertyCount];
+        for (int i = 0; i < properties.length; i++) {
+            RecordId propertyNameId = readRecordId(offset);
+            offset += Segment.RECORD_ID_BYTES;
+            byte type = readByte(offset++);
+            properties[i] = new PropertyTemplate(
+                    i, readString(propertyNameId),
+                    Type.fromTag(Math.abs(type), type < 0));
+        }
+
+        return new Template(
+                primaryType, mixinTypes, properties, childName);
+    }
+
+    long readLength(RecordId id) {
+        return getSegment(id).readLength(id.getOffset());
+    }
+
+    long readLength(int offset) {
+        return internalReadLength(pos(offset, 1));
+    }
+
+    private long internalReadLength(int pos) {
+        int length = data.get(pos++) & 0xff;
+        if ((length & 0x80) == 0) {
+            return length;
+        } else if ((length & 0x40) == 0) {
+            return ((length & 0x3f) << 8
+                    | data.get(pos++) & 0xff)
+                    + SMALL_LIMIT;
+        } else {
+            return (((long) length & 0x3f) << 56
+                    | ((long) (data.get(pos++) & 0xff)) << 48
+                    | ((long) (data.get(pos++) & 0xff)) << 40
+                    | ((long) (data.get(pos++) & 0xff)) << 32
+                    | ((long) (data.get(pos++) & 0xff)) << 24
+                    | ((long) (data.get(pos++) & 0xff)) << 16
+                    | ((long) (data.get(pos++) & 0xff)) << 8
+                    | ((long) (data.get(pos++) & 0xff)))
+                    + MEDIUM_LIMIT;
+        }
+    }
+
+    SegmentStream readStream(int offset) {
+        RecordId id = new RecordId(uuid, offset);
+        int pos = pos(offset, 1);
+        long length = internalReadLength(pos);
+        if (length < Segment.MEDIUM_LIMIT) {
+            byte[] inline = new byte[(int) length];
+            ByteBuffer buffer = data.duplicate();
+            if (length < Segment.SMALL_LIMIT) {
+                buffer.position(pos + 1);
+            } else {
+                buffer.position(pos + 2);
+            }
+            buffer.get(inline);
+            return new SegmentStream(id, inline);
+        } else {
+            int size = (int) ((length + BLOCK_SIZE - 1) / BLOCK_SIZE);
+            ListRecord list =
+                    new ListRecord(this, internalReadRecordId(pos + 8), size);
+            return new SegmentStream(store, id, list, length);
+        }
+    }
+
+}

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=1535743&r1=1535742&r2=1535743&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 Oct 25 14:11:58 2013
@@ -155,7 +155,7 @@ public class SegmentSizeTest {
 
         SegmentNodeState state = writer.writeNode(builder.getNodeState());
         Segment segment = store.readSegment(state.getRecordId().getSegmentId());
-        assertEquals(26728, segment.size());
+        assertEquals(26728, Segment.WEIGHER.weigh(null, segment));
 
         writer.flush(); // force flushing of the previous segment
 
@@ -163,7 +163,7 @@ public class SegmentSizeTest {
         builder.child("child1000");
         state = writer.writeNode(builder.getNodeState());
         segment = store.readSegment(state.getRecordId().getSegmentId());
-        assertEquals(96, segment.size());
+        assertEquals(96, Segment.WEIGHER.weigh(null, segment));
     }
 
     private int getSize(NodeBuilder builder) {
@@ -171,7 +171,7 @@ public class SegmentSizeTest {
         SegmentWriter writer = store.getWriter();
         RecordId id = writer.writeNode(builder.getNodeState()).getRecordId();
         Segment segment = store.readSegment(id.getSegmentId());
-        return segment.size();
+        return Segment.WEIGHER.weigh(null, segment);
     }
 
     private int getAmortizedSize(NodeBuilder builder) {
@@ -179,14 +179,15 @@ public class SegmentSizeTest {
         SegmentWriter writer = store.getWriter();
         NodeState state = builder.getNodeState();
         RecordId id = writer.writeNode(state).getRecordId();
-        int base = store.readSegment(id.getSegmentId()).size();
+        Segment segment = store.readSegment(id.getSegmentId());
+        int base = Segment.WEIGHER.weigh(null, segment);
 
         store = new MemoryStore(); // avoid cross-segment caching
         writer = store.getWriter();
         writer.writeNode(state);
         id = writer.writeNode(state).getRecordId();
-        Segment segment = store.readSegment(id.getSegmentId());
-        return segment.size() - base;
+        segment = store.readSegment(id.getSegmentId());
+        return Segment.WEIGHER.weigh(null, segment) - base;
     }
 
 }