2222import static com .google .common .base .Preconditions .checkNotNull ;
2323import static com .google .common .base .Preconditions .checkPositionIndexes ;
2424import static com .google .common .base .Preconditions .checkState ;
25- import static com .google .common .collect .Lists . newArrayListWithCapacity ;
25+ import static com .google .common .collect .Maps . newHashMap ;
2626import static org .apache .jackrabbit .oak .commons .IOUtils .closeQuietly ;
2727import static org .apache .jackrabbit .oak .segment .SegmentId .isDataSegmentId ;
2828import static org .apache .jackrabbit .oak .segment .SegmentVersion .LATEST_VERSION ;
3434import java .io .StringWriter ;
3535import java .nio .ByteBuffer ;
3636import java .util .Arrays ;
37- import java .util .List ;
37+ import java .util .Map ;
3838import java .util .UUID ;
3939
4040import javax .annotation .CheckForNull ;
5757 */
5858public class Segment {
5959
60+ static final int HEADER_SIZE = 18 ;
61+
6062 /**
6163 * Number of bytes used for storing a record identifier. One byte
6264 * is used for identifying the segment and two for the record offset
6365 * within that segment.
6466 */
65- static final int RECORD_ID_BYTES = 1 + 2 ;
67+ static final int RECORD_ID_BYTES = 8 + 8 + 2 ;
6668
6769 /**
6870 * The limit on segment references within one segment. Since record
@@ -111,12 +113,12 @@ public class Segment {
111113 */
112114 public static final int BLOB_ID_SMALL_LIMIT = 1 << 12 ;
113115
114- public static final int REF_COUNT_OFFSET = 5 ;
115-
116116 static final int ROOT_COUNT_OFFSET = 6 ;
117117
118118 public static final int GC_GENERATION_OFFSET = 10 ;
119119
120+ public static final int REFERENCED_SEGMENT_ID_COUNT_OFFSET = 14 ;
121+
120122 @ Nonnull
121123 private final SegmentStore store ;
122124
@@ -135,12 +137,7 @@ public class Segment {
135137 @ Nonnull
136138 private final SegmentVersion version ;
137139
138- /**
139- * Referenced segment identifiers. Entries are initialized lazily in
140- * {@link #getRefId(int)}. Set to {@code null} for bulk segments.
141- */
142- @ CheckForNull
143- private final SegmentId [] refids ;
140+ private final Map <Integer , RecordId > recordIdCache = newHashMap ();
144141
145142 /**
146143 * Unpacks a 4 byte aligned segment offset.
@@ -193,11 +190,8 @@ public String toString() {
193190 + toHex (data .array ());
194191 }
195192 });
196- this .refids = new SegmentId [getRefCount ()];
197- this .refids [0 ] = id ;
198193 this .version = SegmentVersion .fromByte (segmentVersion );
199194 } else {
200- this .refids = null ;
201195 this .version = LATEST_VERSION ;
202196 }
203197 }
@@ -223,8 +217,6 @@ private static String toHex(byte[] bytes) {
223217 this .id = store .newDataSegmentId ();
224218 this .info = checkNotNull (info );
225219 this .data = ByteBuffer .wrap (checkNotNull (buffer ));
226- this .refids = new SegmentId [SEGMENT_REFERENCE_LIMIT + 1 ];
227- this .refids [0 ] = id ;
228220 this .version = SegmentVersion .fromByte (buffer [3 ]);
229221 id .loaded (this );
230222 }
@@ -253,14 +245,28 @@ public SegmentId getSegmentId() {
253245 return id ;
254246 }
255247
256- int getRefCount () {
257- return (data .get (REF_COUNT_OFFSET ) & 0xff ) + 1 ;
258- }
259-
260248 public int getRootCount () {
261249 return data .getShort (ROOT_COUNT_OFFSET ) & 0xffff ;
262250 }
263251
252+ public int getReferencedSegmentIdCount () {
253+ return data .getInt (REFERENCED_SEGMENT_ID_COUNT_OFFSET );
254+ }
255+
256+ public UUID getReferencedSegmentId (int index ) {
257+ checkArgument (index < getReferencedSegmentIdCount ());
258+
259+ int position = data .position ();
260+
261+ position += HEADER_SIZE ;
262+ position += index * 16 ;
263+
264+ long msb = data .getLong (position );
265+ long lsb = data .getLong (position + 8 );
266+
267+ return new UUID (msb , lsb );
268+ }
269+
264270 /**
265271 * Determine the gc generation a segment from its data. Note that bulk segments don't have
266272 * generations (i.e. stay at 0).
@@ -285,16 +291,28 @@ public int getGcGeneration() {
285291 }
286292
287293 public RecordType getRootType (int index ) {
288- int refCount = getRefCount ();
289294 checkArgument (index < getRootCount ());
290- return RecordType .values ()[data .get (data .position () + refCount * 16 + index * 3 ) & 0xff ];
295+
296+ int position = data .position ();
297+
298+ position += HEADER_SIZE ;
299+ position += getReferencedSegmentIdCount () * 16 ;
300+ position += index * 3 ;
301+
302+ return RecordType .values ()[data .get (position ) & 0xff ];
291303 }
292304
293305 public int getRootOffset (int index ) {
294- int refCount = getRefCount ();
295306 checkArgument (index < getRootCount ());
296- return (data .getShort (data .position () + refCount * 16 + index * 3 + 1 ) & 0xffff )
297- << RECORD_ALIGN_BITS ;
307+
308+ int position = data .position ();
309+
310+ position += HEADER_SIZE ;
311+ position += getReferencedSegmentIdCount () * 16 ;
312+ position += index * 3 ;
313+ position += 1 ;
314+
315+ return (data .getShort (position ) & 0xffff ) << RECORD_ALIGN_BITS ;
298316 }
299317
300318 private volatile String info ;
@@ -316,48 +334,12 @@ public int getRootOffset(int index) {
316334 */
317335 @ CheckForNull
318336 public String getSegmentInfo () {
319- if (info == null && getRefCount () != 0 ) {
337+ if (info == null && id . isDataSegmentId () ) {
320338 info = readString (getRootOffset (0 ));
321339 }
322340 return info ;
323341 }
324342
325- SegmentId getRefId (int index ) {
326- if (refids == null || index >= refids .length ) {
327- String type = "data" ;
328- if (!id .isDataSegmentId ()) {
329- type = "bulk" ;
330- }
331- long delta = System .currentTimeMillis () - id .getCreationTime ();
332- throw new IllegalStateException ("RefId '" + index
333- + "' doesn't exist in " + type + " segment " + id
334- + ". Creation date delta is " + delta + " ms." );
335- }
336- SegmentId refid = refids [index ];
337- if (refid == null ) {
338- synchronized (this ) {
339- refid = refids [index ];
340- if (refid == null ) {
341- int refpos = data .position () + index * 16 ;
342- long msb = data .getLong (refpos );
343- long lsb = data .getLong (refpos + 8 );
344- refid = store .newSegmentId (msb , lsb );
345- refids [index ] = refid ;
346- }
347- }
348- }
349- return refid ;
350- }
351-
352- public List <SegmentId > getReferencedIds () {
353- int refcount = getRefCount ();
354- List <SegmentId > ids = newArrayListWithCapacity (refcount );
355- for (int refid = 0 ; refid < refcount ; refid ++) {
356- ids .add (getRefId (refid ));
357- }
358- return ids ;
359- }
360-
361343 public int size () {
362344 return data .remaining ();
363345 }
@@ -401,9 +383,28 @@ RecordId readRecordId(int offset) {
401383 }
402384
403385 private RecordId internalReadRecordId (int pos ) {
404- SegmentId refid = getRefId (data .get (pos ) & 0xff );
405- int offset = ((data .get (pos + 1 ) & 0xff ) << 8 ) | (data .get (pos + 2 ) & 0xff );
406- return new RecordId (refid , offset << RECORD_ALIGN_BITS );
386+ RecordId recordId = recordIdCache .get (pos );
387+
388+ if (recordId != null ) {
389+ return recordId ;
390+ }
391+
392+ synchronized (recordIdCache ) {
393+ recordId = recordIdCache .get (pos );
394+
395+ if (recordId != null ) {
396+ return recordId ;
397+ }
398+
399+ long msb = data .getLong (pos );
400+ long lsb = data .getLong (pos + 8 );
401+ int offset = (data .getShort (pos + 16 ) & 0xffff ) << RECORD_ALIGN_BITS ;
402+ recordId = new RecordId (store .newSegmentId (msb , lsb ), offset );
403+
404+ recordIdCache .put (pos , recordId );
405+
406+ return recordId ;
407+ }
407408 }
408409
409410 @ Nonnull
@@ -538,17 +539,13 @@ public String toString() {
538539 }
539540 if (id .isDataSegmentId ()) {
540541 writer .println ("--------------------------------------------------------------------------" );
541- int refcount = getRefCount ();
542- for (int refid = 0 ; refid < refcount ; refid ++) {
543- writer .format ("reference %02x: %s%n" , refid , getRefId ( refid ));
542+
543+ for (int i = 0 ; i < getReferencedSegmentIdCount (); i ++) {
544+ writer .format ("reference %02x: %s%n" , i , getReferencedSegmentId ( i ));
544545 }
545- int rootcount = data .getShort (ROOT_COUNT_OFFSET ) & 0xffff ;
546- int pos = data .position () + refcount * 16 ;
547- for (int rootid = 0 ; rootid < rootcount ; rootid ++) {
548- writer .format (
549- "root %d: %s at %04x%n" , rootid ,
550- RecordType .values ()[data .get (pos + rootid * 3 ) & 0xff ],
551- data .getShort (pos + rootid * 3 + 1 ) & 0xffff );
546+
547+ for (int i = 0 ; i < getRootCount (); i ++) {
548+ writer .format ("root %d: %s at %04x%n" , i , getRootType (i ), getRootOffset (i ));
552549 }
553550 }
554551 writer .println ("--------------------------------------------------------------------------" );
0 commit comments