Skip to content

Commit 68a6fa2

Browse files
committed
vendor dependency
1 parent c36f795 commit 68a6fa2

File tree

2 files changed

+204
-11
lines changed

2 files changed

+204
-11
lines changed
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.messaging;
16+
17+
import static java.lang.Math.max;
18+
import static java.lang.Math.min;
19+
20+
import java.io.FilterInputStream;
21+
import java.io.IOException;
22+
import java.io.InputStream;
23+
import java.util.ArrayDeque;
24+
import java.util.Arrays;
25+
import java.util.Queue;
26+
27+
/** @hide */
28+
public final class ByteStreams {
29+
30+
private static final int BUFFER_SIZE = 8192;
31+
32+
/** Creates a new byte array for buffering reads or writes. */
33+
static byte[] createBuffer() {
34+
return new byte[BUFFER_SIZE];
35+
}
36+
37+
private static int saturatedCast(long value) {
38+
if (value > Integer.MAX_VALUE) {
39+
return Integer.MAX_VALUE;
40+
}
41+
if (value < Integer.MIN_VALUE) {
42+
return Integer.MIN_VALUE;
43+
}
44+
return (int) value;
45+
}
46+
47+
private ByteStreams() {}
48+
49+
/** Max array length on JVM. */
50+
private static final int MAX_ARRAY_LEN = Integer.MAX_VALUE - 8;
51+
52+
/** Large enough to never need to expand, given the geometric progression of buffer sizes. */
53+
private static final int TO_BYTE_ARRAY_DEQUE_SIZE = 20;
54+
55+
/**
56+
* Returns a byte array containing the bytes from the buffers already in {@code bufs} (which have
57+
* a total combined length of {@code totalLen} bytes) followed by all bytes remaining in the given
58+
* input stream.
59+
*/
60+
private static byte[] toByteArrayInternal(InputStream in, Queue<byte[]> bufs, int totalLen)
61+
throws IOException {
62+
// Roughly size to match what has been read already. Some file systems, such as procfs, return 0
63+
// as their length. These files are very small, so it's wasteful to allocate an 8KB buffer.
64+
int initialBufferSize = min(BUFFER_SIZE, max(128, Integer.highestOneBit(totalLen) * 2));
65+
// Starting with an 8k buffer, double the size of each successive buffer. Smaller buffers
66+
// quadruple in size until they reach 8k, to minimize the number of small reads for longer
67+
// streams. Buffers are retained in a deque so that there's no copying between buffers while
68+
// reading and so all of the bytes in each new allocated buffer are available for reading from
69+
// the stream.
70+
for (int bufSize = initialBufferSize;
71+
totalLen < MAX_ARRAY_LEN;
72+
bufSize = saturatedCast((long) bufSize * (bufSize < 4096 ? 4 : 2))) {
73+
byte[] buf = new byte[min(bufSize, MAX_ARRAY_LEN - totalLen)];
74+
bufs.add(buf);
75+
int off = 0;
76+
while (off < buf.length) {
77+
// always OK to fill buf; its size plus the rest of bufs is never more than MAX_ARRAY_LEN
78+
int r = in.read(buf, off, buf.length - off);
79+
if (r == -1) {
80+
return combineBuffers(bufs, totalLen);
81+
}
82+
off += r;
83+
totalLen += r;
84+
}
85+
}
86+
87+
// read MAX_ARRAY_LEN bytes without seeing end of stream
88+
if (in.read() == -1) {
89+
// oh, there's the end of the stream
90+
return combineBuffers(bufs, MAX_ARRAY_LEN);
91+
} else {
92+
throw new OutOfMemoryError("input is too large to fit in a byte array");
93+
}
94+
}
95+
96+
private static byte[] combineBuffers(Queue<byte[]> bufs, int totalLen) {
97+
if (bufs.isEmpty()) {
98+
return new byte[0];
99+
}
100+
byte[] result = bufs.remove();
101+
if (result.length == totalLen) {
102+
return result;
103+
}
104+
int remaining = totalLen - result.length;
105+
result = Arrays.copyOf(result, totalLen);
106+
while (remaining > 0) {
107+
byte[] buf = bufs.remove();
108+
int bytesToCopy = min(remaining, buf.length);
109+
int resultOffset = totalLen - remaining;
110+
System.arraycopy(buf, 0, result, resultOffset, bytesToCopy);
111+
remaining -= bytesToCopy;
112+
}
113+
return result;
114+
}
115+
116+
/**
117+
* Reads all bytes from an input stream into a byte array. Does not close the stream.
118+
*
119+
* @param in the input stream to read from
120+
* @return a byte array containing all the bytes from the stream
121+
* @throws IOException if an I/O error occurs
122+
*/
123+
public static byte[] toByteArray(InputStream in) throws IOException {
124+
return toByteArrayInternal(in, new ArrayDeque<byte[]>(TO_BYTE_ARRAY_DEQUE_SIZE), 0);
125+
}
126+
127+
public static InputStream limit(InputStream in, long limit) {
128+
return new LimitedInputStream(in, limit);
129+
}
130+
131+
private static final class LimitedInputStream extends FilterInputStream {
132+
133+
private long left;
134+
private long mark = -1;
135+
136+
LimitedInputStream(InputStream in, long limit) {
137+
super(in);
138+
left = limit;
139+
}
140+
141+
@Override
142+
public int available() throws IOException {
143+
return (int) Math.min(in.available(), left);
144+
}
145+
146+
// it's okay to mark even if mark isn't supported, as reset won't work
147+
@Override
148+
public synchronized void mark(int readLimit) {
149+
in.mark(readLimit);
150+
mark = left;
151+
}
152+
153+
@Override
154+
public int read() throws IOException {
155+
if (left == 0) {
156+
return -1;
157+
}
158+
159+
int result = in.read();
160+
if (result != -1) {
161+
--left;
162+
}
163+
return result;
164+
}
165+
166+
@Override
167+
public int read(byte[] b, int off, int len) throws IOException {
168+
if (left == 0) {
169+
return -1;
170+
}
171+
172+
len = (int) Math.min(len, left);
173+
int result = in.read(b, off, len);
174+
if (result != -1) {
175+
left -= result;
176+
}
177+
return result;
178+
}
179+
180+
@Override
181+
public synchronized void reset() throws IOException {
182+
if (!in.markSupported()) {
183+
throw new IOException("Mark not supported");
184+
}
185+
if (mark == -1) {
186+
throw new IOException("Mark not set");
187+
}
188+
189+
in.reset();
190+
left = mark;
191+
}
192+
193+
@Override
194+
public long skip(long n) throws IOException {
195+
n = Math.min(n, left);
196+
long skipped = in.skip(n);
197+
left -= skipped;
198+
return skipped;
199+
}
200+
}
201+
}

firebase-messaging/src/main/java/com/google/firebase/messaging/ImageDownload.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import androidx.annotation.Nullable;
2424
import com.google.android.gms.tasks.Task;
2525
import com.google.android.gms.tasks.TaskCompletionSource;
26-
import java.io.ByteArrayOutputStream;
2726
import java.io.Closeable;
2827
import java.io.IOException;
2928
import java.io.InputStream;
@@ -115,16 +114,9 @@ private byte[] blockingDownloadBytes() throws IOException {
115114
try (InputStream connectionInputStream = connection.getInputStream()) {
116115
// Read one byte over the limit so we can tell if the data is too big, as in many cases
117116
// BitmapFactory will happily decode a partial image.
118-
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
119-
120-
int nRead;
121-
byte[] data = new byte[MAX_IMAGE_SIZE_BYTES + 1];
122-
123-
while ((nRead = connectionInputStream.read(data, 0, data.length)) != -1) {
124-
buffer.write(data, 0, nRead);
125-
}
126-
127-
bytes = buffer.toByteArray();
117+
bytes =
118+
ByteStreams.toByteArray(
119+
ByteStreams.limit(connectionInputStream, MAX_IMAGE_SIZE_BYTES + 1));
128120
}
129121

130122
if (Log.isLoggable(TAG, Log.VERBOSE)) {

0 commit comments

Comments
 (0)