com.bumptech.glide.util.ByteBufferUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of glide Show documentation
Show all versions of glide Show documentation
A fast and efficient image loading library for Android focused on smooth scrolling.
package com.bumptech.glide.util;
import android.support.annotation.NonNull;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.atomic.AtomicReference;
/**
* Utilities for interacting with {@link java.nio.ByteBuffer}s.
*/
// Public API.
@SuppressWarnings({"unused", "WeakerAccess"})
public final class ByteBufferUtil {
// 16 Kb
private static final int BUFFER_SIZE = 16384;
private static final AtomicReference BUFFER_REF = new AtomicReference<>();
private ByteBufferUtil() {
// Utility class.
}
public static ByteBuffer fromFile(File file) throws IOException {
RandomAccessFile raf = null;
FileChannel channel = null;
try {
long fileLength = file.length();
// See #2240.
if (fileLength > Integer.MAX_VALUE) {
throw new IOException("File too large to map into memory");
}
// See b/67710449.
if (fileLength == 0) {
throw new IOException("File unsuitable for memory mapping");
}
raf = new RandomAccessFile(file, "r");
channel = raf.getChannel();
return channel.map(FileChannel.MapMode.READ_ONLY, 0, fileLength).load();
} finally {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
// Ignored.
}
}
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
// Ignored.
}
}
}
}
public static void toFile(ByteBuffer buffer, File file) throws IOException {
buffer.position(0);
RandomAccessFile raf = null;
FileChannel channel = null;
try {
raf = new RandomAccessFile(file, "rw");
channel = raf.getChannel();
channel.write(buffer);
channel.force(false /*metadata*/);
channel.close();
raf.close();
} finally {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
// Ignored.
}
}
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
// Ignored.
}
}
}
}
public static void toStream(ByteBuffer byteBuffer, OutputStream os) throws IOException {
SafeArray safeArray = getSafeArray(byteBuffer);
if (safeArray != null) {
os.write(safeArray.data, safeArray.offset, safeArray.offset + safeArray.limit);
} else {
byte[] buffer = BUFFER_REF.getAndSet(null);
if (buffer == null) {
buffer = new byte[BUFFER_SIZE];
}
while (byteBuffer.remaining() > 0) {
int toRead = Math.min(byteBuffer.remaining(), buffer.length);
byteBuffer.get(buffer, 0 /*dstOffset*/, toRead /*byteCount*/);
os.write(buffer, 0, toRead);
}
BUFFER_REF.set(buffer);
}
}
public static byte[] toBytes(ByteBuffer byteBuffer) {
final byte[] result;
SafeArray safeArray = getSafeArray(byteBuffer);
if (safeArray != null && safeArray.offset == 0 && safeArray.limit == safeArray.data.length) {
result = byteBuffer.array();
} else {
ByteBuffer toCopy = byteBuffer.asReadOnlyBuffer();
result = new byte[toCopy.limit()];
toCopy.position(0);
toCopy.get(result);
}
return result;
}
public static InputStream toStream(ByteBuffer buffer) {
return new ByteBufferStream(buffer);
}
public static ByteBuffer fromStream(InputStream stream) throws IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream(BUFFER_SIZE);
byte[] buffer = BUFFER_REF.getAndSet(null);
if (buffer == null) {
buffer = new byte[BUFFER_SIZE];
}
int n;
while ((n = stream.read(buffer)) >= 0) {
outStream.write(buffer, 0, n);
}
BUFFER_REF.set(buffer);
byte[] bytes = outStream.toByteArray();
// Some resource decoders require a direct byte buffer. Prefer allocateDirect() over wrap()
return (ByteBuffer) ByteBuffer.allocateDirect(bytes.length).put(bytes).position(0);
}
private static SafeArray getSafeArray(ByteBuffer byteBuffer) {
if (!byteBuffer.isReadOnly() && byteBuffer.hasArray()) {
return new SafeArray(byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.limit());
}
return null;
}
static final class SafeArray {
@Synthetic final int offset;
@Synthetic final int limit;
@Synthetic final byte[] data;
SafeArray(byte[] data, int offset, int limit) {
this.data = data;
this.offset = offset;
this.limit = limit;
}
}
private static class ByteBufferStream extends InputStream {
private static final int UNSET = -1;
private final ByteBuffer byteBuffer;
private int markPos = UNSET;
ByteBufferStream(ByteBuffer byteBuffer) {
this.byteBuffer = byteBuffer;
}
@Override
public int available() throws IOException {
return byteBuffer.remaining();
}
@Override
public int read() throws IOException {
if (!byteBuffer.hasRemaining()) {
return -1;
}
return byteBuffer.get();
}
@Override
public synchronized void mark(int readLimit) {
markPos = byteBuffer.position();
}
@Override
public boolean markSupported() {
return true;
}
@Override
public int read(@NonNull byte[] buffer, int byteOffset, int byteCount) throws IOException {
if (!byteBuffer.hasRemaining()) {
return -1;
}
int toRead = Math.min(byteCount, available());
byteBuffer.get(buffer, byteOffset, toRead);
return toRead;
}
@Override
public synchronized void reset() throws IOException {
if (markPos == UNSET) {
throw new IOException("Cannot reset to unset mark position");
}
// reset() was not implemented correctly in 4.0.4, so we track the mark position ourselves.
byteBuffer.position(markPos);
}
@Override
public long skip(long byteCount) throws IOException {
if (!byteBuffer.hasRemaining()) {
return -1;
}
long toSkip = Math.min(byteCount, available());
byteBuffer.position((int) (byteBuffer.position() + toSkip));
return toSkip;
}
}
}