Skip to content

Commit 2468e98

Browse files
authored
ByteBufBsonDocument remains a ByteBuf (#1119)
* Ensure that ByteBufBsonDocument never hydrates itself into a HashMap * Add ByteBufBsonArray and ensure it never hydrates itself into an ArrayList JAVA-4917
1 parent 05f2ffd commit 2468e98

File tree

6 files changed

+819
-43
lines changed

6 files changed

+819
-43
lines changed

bson/src/main/org/bson/BsonArray.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ public int hashCode() {
237237
@Override
238238
public String toString() {
239239
return "BsonArray{"
240-
+ "values=" + values
240+
+ "values=" + getValues()
241241
+ '}';
242242
}
243243

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.internal.connection;
18+
19+
import org.bson.BsonArray;
20+
import org.bson.BsonBinaryReader;
21+
import org.bson.BsonType;
22+
import org.bson.BsonValue;
23+
import org.bson.ByteBuf;
24+
import org.bson.io.ByteBufferBsonInput;
25+
26+
import java.util.ArrayList;
27+
import java.util.Collection;
28+
import java.util.Iterator;
29+
import java.util.List;
30+
import java.util.ListIterator;
31+
import java.util.NoSuchElementException;
32+
import java.util.Objects;
33+
34+
import static com.mongodb.internal.connection.ByteBufBsonHelper.readBsonValue;
35+
36+
final class ByteBufBsonArray extends BsonArray {
37+
private final ByteBuf byteBuf;
38+
39+
ByteBufBsonArray(final ByteBuf byteBuf) {
40+
this.byteBuf = byteBuf;
41+
}
42+
43+
@Override
44+
public Iterator<BsonValue> iterator() {
45+
return new ByteBufBsonArrayIterator();
46+
}
47+
48+
@Override
49+
public List<BsonValue> getValues() {
50+
List<BsonValue> values = new ArrayList<>();
51+
for (BsonValue cur: this) {
52+
//noinspection UseBulkOperation
53+
values.add(cur);
54+
}
55+
return values;
56+
}
57+
58+
private static final String READ_ONLY_MESSAGE = "This BsonArray instance is read-only";
59+
60+
@Override
61+
public int size() {
62+
int size = 0;
63+
for (BsonValue ignored : this) {
64+
size++;
65+
}
66+
return size;
67+
}
68+
69+
@Override
70+
public boolean isEmpty() {
71+
return !iterator().hasNext();
72+
}
73+
74+
@Override
75+
public boolean equals(final Object o) {
76+
if (o == this) {
77+
return true;
78+
}
79+
if (!(o instanceof List)) {
80+
return false;
81+
}
82+
Iterator<BsonValue> e1 = iterator();
83+
Iterator<?> e2 = ((List<?>) o).iterator();
84+
while (e1.hasNext() && e2.hasNext()) {
85+
if (!(Objects.equals(e1.next(), e2.next()))) {
86+
return false;
87+
}
88+
}
89+
return !(e1.hasNext() || e2.hasNext());
90+
}
91+
92+
@Override
93+
public int hashCode() {
94+
int hashCode = 1;
95+
for (BsonValue cur : this) {
96+
hashCode = 31 * hashCode + (cur == null ? 0 : cur.hashCode());
97+
}
98+
return hashCode;
99+
}
100+
101+
@Override
102+
public boolean contains(final Object o) {
103+
for (BsonValue cur : this) {
104+
if (Objects.equals(o, cur)) {
105+
return true;
106+
}
107+
}
108+
109+
return false;
110+
}
111+
112+
@Override
113+
public Object[] toArray() {
114+
Object[] retVal = new Object[size()];
115+
Iterator<BsonValue> it = iterator();
116+
for (int i = 0; i < retVal.length; i++) {
117+
retVal[i] = it.next();
118+
}
119+
return retVal;
120+
}
121+
122+
@Override
123+
@SuppressWarnings("unchecked")
124+
public <T> T[] toArray(final T[] a) {
125+
int size = size();
126+
T[] retVal = a.length >= size ? a : (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
127+
Iterator<BsonValue> it = iterator();
128+
for (int i = 0; i < retVal.length; i++) {
129+
retVal[i] = (T) it.next();
130+
}
131+
return retVal;
132+
}
133+
134+
@Override
135+
public boolean containsAll(final Collection<?> c) {
136+
for (Object e : c) {
137+
if (!contains(e)) {
138+
return false;
139+
}
140+
}
141+
return true;
142+
}
143+
144+
@Override
145+
public BsonValue get(final int index) {
146+
if (index < 0) {
147+
throw new IndexOutOfBoundsException("Index out of range: " + index);
148+
}
149+
150+
int i = 0;
151+
for (BsonValue cur : this) {
152+
if (i++ == index) {
153+
return cur;
154+
}
155+
}
156+
157+
throw new IndexOutOfBoundsException("Index out of range: " + index);
158+
}
159+
160+
@Override
161+
public int indexOf(final Object o) {
162+
int i = 0;
163+
for (BsonValue cur : this) {
164+
if (Objects.equals(o, cur)) {
165+
return i;
166+
}
167+
i++;
168+
}
169+
170+
return -1;
171+
}
172+
173+
@Override
174+
public int lastIndexOf(final Object o) {
175+
ListIterator<BsonValue> listIterator = listIterator(size());
176+
while (listIterator.hasPrevious()) {
177+
if (Objects.equals(o, listIterator.previous())) {
178+
return listIterator.nextIndex();
179+
}
180+
}
181+
return -1;
182+
}
183+
184+
@Override
185+
public ListIterator<BsonValue> listIterator() {
186+
return listIterator(0);
187+
}
188+
189+
@Override
190+
public ListIterator<BsonValue> listIterator(final int index) {
191+
// Not the most efficient way to do this, but unlikely anyone will notice in practice
192+
return new ArrayList<>(this).listIterator(index);
193+
}
194+
195+
@Override
196+
public List<BsonValue> subList(final int fromIndex, final int toIndex) {
197+
if (fromIndex < 0) {
198+
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
199+
}
200+
if (fromIndex > toIndex) {
201+
throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
202+
}
203+
List<BsonValue> subList = new ArrayList<>();
204+
int i = 0;
205+
for (BsonValue cur: this) {
206+
if (i == toIndex) {
207+
break;
208+
}
209+
if (i >= fromIndex) {
210+
subList.add(cur);
211+
}
212+
i++;
213+
}
214+
if (toIndex > i) {
215+
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
216+
}
217+
return subList;
218+
}
219+
220+
@Override
221+
public boolean add(final BsonValue bsonValue) {
222+
throw new UnsupportedOperationException(READ_ONLY_MESSAGE);
223+
}
224+
225+
@Override
226+
public boolean remove(final Object o) {
227+
throw new UnsupportedOperationException(READ_ONLY_MESSAGE);
228+
}
229+
230+
@Override
231+
public boolean addAll(final Collection<? extends BsonValue> c) {
232+
throw new UnsupportedOperationException(READ_ONLY_MESSAGE);
233+
}
234+
235+
@Override
236+
public boolean addAll(final int index, final Collection<? extends BsonValue> c) {
237+
throw new UnsupportedOperationException(READ_ONLY_MESSAGE);
238+
}
239+
240+
@Override
241+
public boolean removeAll(final Collection<?> c) {
242+
throw new UnsupportedOperationException(READ_ONLY_MESSAGE);
243+
}
244+
245+
@Override
246+
public boolean retainAll(final Collection<?> c) {
247+
throw new UnsupportedOperationException(READ_ONLY_MESSAGE);
248+
}
249+
250+
@Override
251+
public void clear() {
252+
throw new UnsupportedOperationException(READ_ONLY_MESSAGE);
253+
}
254+
255+
@Override
256+
public BsonValue set(final int index, final BsonValue element) {
257+
throw new UnsupportedOperationException(READ_ONLY_MESSAGE);
258+
}
259+
260+
@Override
261+
public void add(final int index, final BsonValue element) {
262+
throw new UnsupportedOperationException(READ_ONLY_MESSAGE);
263+
}
264+
265+
@Override
266+
public BsonValue remove(final int index) {
267+
throw new UnsupportedOperationException(READ_ONLY_MESSAGE);
268+
}
269+
270+
private class ByteBufBsonArrayIterator implements Iterator<BsonValue> {
271+
private final ByteBuf duplicatedByteBuf = byteBuf.duplicate();
272+
private final BsonBinaryReader bsonReader;
273+
274+
{
275+
bsonReader = new BsonBinaryReader(new ByteBufferBsonInput(duplicatedByteBuf));
276+
// While one might expect that this would be a call to BsonReader#readStartArray that doesn't work because BsonBinaryReader
277+
// expects to be positioned at the start at the beginning of a document, not an array. Fortunately, a BSON array has exactly
278+
// the same structure as a BSON document (the keys are just the array indices converted to a strings). So it works fine to
279+
// call BsonReader#readStartDocument here, and just skip all the names via BsonReader#skipName below.
280+
bsonReader.readStartDocument();
281+
bsonReader.readBsonType();
282+
}
283+
284+
@Override
285+
public boolean hasNext() {
286+
return bsonReader.getCurrentBsonType() != BsonType.END_OF_DOCUMENT;
287+
}
288+
289+
@Override
290+
public BsonValue next() {
291+
if (!hasNext()) {
292+
throw new NoSuchElementException();
293+
}
294+
bsonReader.skipName();
295+
BsonValue value = readBsonValue(duplicatedByteBuf, bsonReader);
296+
bsonReader.readBsonType();
297+
return value;
298+
}
299+
}
300+
}

0 commit comments

Comments
 (0)