001/* 002 * (C) Copyright 2015-2020 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.core.blob; 020 021import java.io.EOFException; 022import java.io.IOException; 023import java.io.InputStream; 024 025import org.apache.commons.io.input.BoundedInputStream; 026 027/** 028 * A byte range. 029 * 030 * @since 11.1 031 */ 032public class ByteRange { 033 034 protected final long start; 035 036 /** The end is inclusive. */ 037 protected final long end; 038 039 protected ByteRange(long start, long end) { 040 this.start = start; 041 this.end = end; 042 } 043 044 /** 045 * Constructs a byte range from a start and INCLUSIVE end. 046 * 047 * @param start the start 048 * @param end the INCLUSIVE end 049 */ 050 public static ByteRange inclusive(long start, long end) { 051 return new ByteRange(start, end); 052 } 053 054 /** The start. */ 055 public long getStart() { 056 return start; 057 } 058 059 /** The end, which is INCLUSIVE. */ 060 public long getEnd() { 061 return end; 062 } 063 064 /** The length. */ 065 public long getLength() { 066 return end - start + 1; 067 } 068 069 /** 070 * Returns a sub-stream of the given stream to which this byte range has been applied. 071 * 072 * @param stream the stream 073 * @return a sub-stream for this byte range 074 */ 075 public InputStream forStream(InputStream stream) throws IOException { 076 try { 077 // avoid using IOUtils.skipFully, which uses read() under the hood 078 long remain = getStart(); 079 while (remain > 0) { 080 long n = stream.skip(remain); 081 if (n < 0) { 082 throw new EOFException(); 083 } 084 if (n == 0) { 085 throw new IOException("Failed to skip in stream"); 086 } 087 remain -= n; 088 } 089 } catch (IOException e) { 090 stream.close(); 091 throw e; 092 } 093 return new BoundedInputStream(stream, getLength()); 094 } 095 096 @Override 097 public String toString() { 098 return getClass().getSimpleName() + '(' + start + '-' + end + ')'; 099 } 100 101}