| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed 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. |
| */ |
| |
| #define LOG_TAG "Stream" |
| |
| #include "Stream.h" |
| |
| #include <string.h> |
| |
| #include "JNIHelpers.h" |
| #include "utils/log.h" |
| #include "utils/math.h" |
| |
| static struct { |
| jmethodID read; |
| jmethodID reset; |
| } gInputStreamClassInfo; |
| |
| Stream::Stream() |
| : mPeekBuffer(0) |
| , mPeekSize(0) |
| , mPeekOffset(0) { |
| } |
| |
| Stream::~Stream() { |
| delete mPeekBuffer; |
| } |
| |
| size_t Stream::peek(void* buffer, size_t size) { |
| size_t peek_remaining = mPeekSize - mPeekOffset; |
| if (size > peek_remaining) { |
| char* old_peek = mPeekBuffer; |
| mPeekBuffer = new char[size]; |
| if (old_peek) { |
| memcpy(mPeekBuffer, old_peek + mPeekOffset, peek_remaining); |
| delete old_peek; |
| } |
| size_t read = doRead(mPeekBuffer + mPeekOffset, size - peek_remaining); |
| mPeekOffset = 0; |
| mPeekSize = peek_remaining + read; |
| } |
| size = min(size, mPeekSize - mPeekOffset); |
| memcpy(buffer, mPeekBuffer + mPeekOffset, size); |
| return size; |
| } |
| |
| size_t Stream::read(void* buffer, size_t size) { |
| size_t bytes_read = 0; |
| size_t peek_remaining = mPeekSize - mPeekOffset; |
| if (peek_remaining) { |
| bytes_read = min(size, peek_remaining); |
| memcpy(buffer, mPeekBuffer + mPeekOffset, bytes_read); |
| mPeekOffset += bytes_read; |
| if (mPeekOffset == mPeekSize) { |
| delete mPeekBuffer; |
| mPeekBuffer = 0; |
| mPeekOffset = 0; |
| mPeekSize = 0; |
| } |
| size -= bytes_read; |
| buffer = ((char*) buffer) + bytes_read; |
| } |
| if (size) { |
| bytes_read += doRead(buffer, size); |
| } |
| return bytes_read; |
| } |
| |
| size_t MemoryStream::doRead(void* buffer, size_t size) { |
| size = min(size, mRemaining); |
| memcpy(buffer, mBuffer, size); |
| mBuffer += size; |
| mRemaining -= size; |
| return size; |
| } |
| |
| size_t FileStream::doRead(void* buffer, size_t size) { |
| return fread(buffer, 1, size, mFd); |
| } |
| |
| size_t JavaInputStream::doRead(void* dstBuffer, size_t size) { |
| size_t totalBytesRead = 0; |
| |
| do { |
| size_t requested = min(size, mByteArrayLength); |
| |
| jint bytesRead = mEnv->CallIntMethod(mInputStream, |
| gInputStreamClassInfo.read, mByteArray, 0, requested); |
| if (mEnv->ExceptionCheck() || bytesRead < 0) { |
| return 0; |
| } |
| |
| mEnv->GetByteArrayRegion(mByteArray, 0, bytesRead, (jbyte*)dstBuffer); |
| dstBuffer = (char*)dstBuffer + bytesRead; |
| totalBytesRead += bytesRead; |
| size -= bytesRead; |
| } while (size > 0); |
| |
| return totalBytesRead; |
| } |
| |
| jint JavaStream_OnLoad(JNIEnv* env) { |
| // Skip the verbose logging on error for these, as they won't be subject |
| // to obfuscators or similar and are thus unlikely to ever fail |
| jclass inputStreamClazz = env->FindClass("java/io/InputStream"); |
| if (!inputStreamClazz) { |
| return -1; |
| } |
| gInputStreamClassInfo.read = env->GetMethodID(inputStreamClazz, "read", "([BII)I"); |
| gInputStreamClassInfo.reset = env->GetMethodID(inputStreamClazz, "reset", "()V"); |
| if (!gInputStreamClassInfo.read || !gInputStreamClassInfo.reset) { |
| return -1; |
| } |
| return 0; |
| } |