/** * 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.hadoop.hive.common.io; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** Java linked list iterator interface is convoluted, and moreover concurrent modifications * of the same list by multiple iterators are impossible. Hence, this. * Java also doesn't support multiple inheritance, so this cannot be done as "aspect"... */ public class DiskRangeList extends DiskRange { private static final Logger LOG = LoggerFactory.getLogger(DiskRangeList.class); public DiskRangeList prev, next; public DiskRangeList(long offset, long end) { super(offset, end); } /** Replaces this element with another in the list; returns the new element. * @param other the disk range to swap into this list * @return the new element */ public DiskRangeList replaceSelfWith(DiskRangeList other) { other.prev = this.prev; other.next = this.next; if (this.prev != null) { this.prev.next = other; } if (this.next != null) { this.next.prev = other; } this.next = this.prev = null; return other; } /** * Inserts an intersecting range before current in the list and adjusts offset accordingly. * @param other the element to insert * @return the new element. */ public DiskRangeList insertPartBefore(DiskRangeList other) { assert other.end >= this.offset; this.offset = other.end; other.prev = this.prev; other.next = this; if (this.prev != null) { this.prev.next = other; } this.prev = other; return other; } /** * Inserts an element after current in the list. * @param other the new element to insert * @return the new element. * */ public DiskRangeList insertAfter(DiskRangeList other) { other.next = this.next; other.prev = this; if (this.next != null) { this.next.prev = other; } this.next = other; return other; } /** * Inserts an intersecting range after current in the list and adjusts offset accordingly. * @param other the new element to insert * @return the new element. */ public DiskRangeList insertPartAfter(DiskRangeList other) { assert other.offset <= this.end; this.end = other.offset; return insertAfter(other); } /** Removes an element after current from the list. */ public void removeAfter() { DiskRangeList other = this.next; this.next = other.next; if (this.next != null) { this.next.prev = this; } other.next = other.prev = null; } /** Removes the current element from the list. */ public void removeSelf() { if (this.prev != null) { this.prev.next = this.next; } if (this.next != null) { this.next.prev = this.prev; } this.next = this.prev = null; } /** Splits current element in the list, using DiskRange::slice. * @param cOffset the position to split the list * @return the split list */ public final DiskRangeList split(long cOffset) { insertAfter((DiskRangeList)this.sliceAndShift(cOffset, end, 0)); return replaceSelfWith((DiskRangeList)this.sliceAndShift(offset, cOffset, 0)); } public boolean hasContiguousNext() { return next != null && end == next.offset; } // @VisibleForTesting public int listSize() { int result = 1; DiskRangeList current = this.next; while (current != null) { ++result; current = current.next; } return result; } public long getTotalLength() { long totalLength = getLength(); DiskRangeList current = next; while (current != null) { totalLength += current.getLength(); current = current.next; } return totalLength; } // @VisibleForTesting public DiskRangeList[] listToArray() { DiskRangeList[] result = new DiskRangeList[listSize()]; int i = 0; DiskRangeList current = this.next; while (current != null) { result[i] = current; ++i; current = current.next; } return result; } public static class CreateHelper { private DiskRangeList tail = null, head; public DiskRangeList getTail() { return tail; } public void addOrMerge(long offset, long end, boolean doMerge, boolean doLogNew) { if (doMerge && tail != null && tail.merge(offset, end)) return; if (doLogNew) { LOG.debug("Creating new range; last range (which can include some previous adds) was " + tail); } DiskRangeList node = new DiskRangeList(offset, end); if (tail == null) { head = tail = node; } else { tail = tail.insertAfter(node); } } public DiskRangeList get() { return head; } public DiskRangeList extract() { DiskRangeList result = head; head = null; return result; } } /** * List in-place mutation helper - a bogus first element that is inserted before list head, * and thus remains constant even if head is replaced with some new range via in-place list * mutation. extract() can be used to obtain the modified list. */ public static class MutateHelper extends DiskRangeList { public MutateHelper(DiskRangeList head) { super(-1, -1); assert head != null; assert head.prev == null; this.next = head; head.prev = this; } public DiskRangeList get() { return next; } public DiskRangeList extract() { DiskRangeList result = this.next; assert result != null; this.next = result.prev = null; return result; } } public void setEnd(long newEnd) { assert newEnd >= this.offset; assert this.next == null || this.next.offset >= newEnd; this.end = newEnd; } }