18
18
19
19
import org .bson .ByteBuf ;
20
20
import org .bson .ByteBufNIO ;
21
+ import org .bson .types .ObjectId ;
21
22
22
23
import java .io .IOException ;
23
24
import java .io .OutputStream ;
25
+ import java .nio .Buffer ;
24
26
import java .nio .ByteBuffer ;
25
27
import java .util .Arrays ;
28
+ import java .util .Collections ;
26
29
import java .util .List ;
27
30
28
31
import static java .lang .String .format ;
32
35
* A BSON output stream that stores the output in a single, un-pooled byte array.
33
36
*/
34
37
public class BasicOutputBuffer extends OutputBuffer {
35
- private byte [] buffer ;
36
- private int position ;
38
+
39
+ /**
40
+ * This ByteBuffer allows us to write ObjectIDs without allocating a temporary array per object, and enables us
41
+ * to leverage JVM intrinsics for writing little-endian numeric values.
42
+ */
43
+ private ByteBuffer buffer ;
37
44
38
45
/**
39
46
* Construct an instance with a default initial byte array size.
@@ -48,7 +55,8 @@ public BasicOutputBuffer() {
48
55
* @param initialSize the initial size of the byte array
49
56
*/
50
57
public BasicOutputBuffer (final int initialSize ) {
51
- buffer = new byte [initialSize ];
58
+ // Allocate heap buffer to ensure we can access underlying array
59
+ buffer = ByteBuffer .allocate (initialSize ).order (LITTLE_ENDIAN );
52
60
}
53
61
54
62
/**
@@ -58,50 +66,76 @@ public BasicOutputBuffer(final int initialSize) {
58
66
* @since 3.3
59
67
*/
60
68
public byte [] getInternalBuffer () {
61
- return buffer ;
69
+ return buffer . array () ;
62
70
}
63
71
64
72
@ Override
65
73
public void write (final byte [] b ) {
74
+ writeBytes (b , 0 , b .length );
75
+ }
76
+
77
+ @ Override
78
+ public byte [] toByteArray () {
79
+ ensureOpen ();
80
+ return Arrays .copyOf (buffer .array (), buffer .position ());
81
+ }
82
+
83
+ @ Override
84
+ public void writeInt32 (final int value ) {
85
+ ensureOpen ();
86
+ ensure (4 );
87
+ buffer .putInt (value );
88
+ }
89
+
90
+ @ Override
91
+ public void writeInt32 (final int position , final int value ) {
92
+ ensureOpen ();
93
+ checkPosition (position , 4 );
94
+ buffer .putInt (position , value );
95
+ }
96
+
97
+ @ Override
98
+ public void writeInt64 (final long value ) {
99
+ ensureOpen ();
100
+ ensure (8 );
101
+ buffer .putLong (value );
102
+ }
103
+
104
+ @ Override
105
+ public void writeObjectId (final ObjectId value ) {
66
106
ensureOpen ();
67
- write (b , 0 , b .length );
107
+ ensure (12 );
108
+ value .putToByteBuffer (buffer );
68
109
}
69
110
70
111
@ Override
71
112
public void writeBytes (final byte [] bytes , final int offset , final int length ) {
72
113
ensureOpen ();
73
114
74
115
ensure (length );
75
- System .arraycopy (bytes , offset , buffer , position , length );
76
- position += length ;
116
+ buffer .put (bytes , offset , length );
77
117
}
78
118
79
119
@ Override
80
120
public void writeByte (final int value ) {
81
121
ensureOpen ();
82
122
83
123
ensure (1 );
84
- buffer [ position ++] = ( byte ) (0xFF & value );
124
+ buffer . put (( byte ) (0xFF & value ) );
85
125
}
86
126
87
127
@ Override
88
128
protected void write (final int absolutePosition , final int value ) {
89
129
ensureOpen ();
130
+ checkPosition (absolutePosition , 1 );
90
131
91
- if (absolutePosition < 0 ) {
92
- throw new IllegalArgumentException (format ("position must be >= 0 but was %d" , absolutePosition ));
93
- }
94
- if (absolutePosition > position - 1 ) {
95
- throw new IllegalArgumentException (format ("position must be <= %d but was %d" , position - 1 , absolutePosition ));
96
- }
97
-
98
- buffer [absolutePosition ] = (byte ) (0xFF & value );
132
+ buffer .put (absolutePosition , (byte ) (0xFF & value ));
99
133
}
100
134
101
135
@ Override
102
136
public int getPosition () {
103
137
ensureOpen ();
104
- return position ;
138
+ return buffer . position () ;
105
139
}
106
140
107
141
/**
@@ -110,29 +144,32 @@ public int getPosition() {
110
144
@ Override
111
145
public int getSize () {
112
146
ensureOpen ();
113
- return position ;
147
+ return buffer . position () ;
114
148
}
115
149
116
150
@ Override
117
151
public int pipe (final OutputStream out ) throws IOException {
118
152
ensureOpen ();
119
- out .write (buffer , 0 , position );
120
- return position ;
153
+ out .write (buffer . array () , 0 , buffer . position () );
154
+ return buffer . position () ;
121
155
}
122
156
123
157
@ Override
124
158
public void truncateToPosition (final int newPosition ) {
125
159
ensureOpen ();
126
- if (newPosition > position || newPosition < 0 ) {
160
+ if (newPosition > buffer . position () || newPosition < 0 ) {
127
161
throw new IllegalArgumentException ();
128
162
}
129
- position = newPosition ;
163
+ // The cast is required for compatibility with JDK 9+ where ByteBuffer's position method is inherited from Buffer.
164
+ ((Buffer ) buffer ).position (newPosition );
130
165
}
131
166
132
167
@ Override
133
168
public List <ByteBuf > getByteBuffers () {
134
169
ensureOpen ();
135
- return Arrays .asList (new ByteBufNIO (ByteBuffer .wrap (buffer , 0 , position ).duplicate ().order (LITTLE_ENDIAN )));
170
+ // Create a flipped copy of the buffer for reading. Note that ByteBufNIO overwrites the endian-ness.
171
+ ByteBuffer flipped = ByteBuffer .wrap (buffer .array (), 0 , buffer .position ());
172
+ return Collections .singletonList (new ByteBufNIO (flipped ));
136
173
}
137
174
138
175
@ Override
@@ -147,19 +184,32 @@ private void ensureOpen() {
147
184
}
148
185
149
186
private void ensure (final int more ) {
150
- int need = position + more ;
151
- if (need <= buffer .length ) {
187
+ int length = buffer .position ();
188
+ int need = length + more ;
189
+ if (need <= buffer .capacity ()) {
152
190
return ;
153
191
}
154
192
155
- int newSize = buffer . length * 2 ;
193
+ int newSize = length * 2 ;
156
194
if (newSize < need ) {
157
195
newSize = need + 128 ;
158
196
}
159
197
160
- byte [] n = new byte [ newSize ] ;
161
- System . arraycopy (buffer , 0 , n , 0 , position );
162
- buffer = n ;
198
+ ByteBuffer tmp = ByteBuffer . allocate ( newSize ). order ( LITTLE_ENDIAN ) ;
199
+ tmp . put (buffer . array () , 0 , length ); // Avoids covariant call to flip on jdk8
200
+ this . buffer = tmp ;
163
201
}
164
202
203
+ /**
204
+ * Ensures that `absolutePosition` is a valid index in `this.buffer` and there is room to write at
205
+ * least `bytesToWrite` bytes.
206
+ */
207
+ private void checkPosition (final int absolutePosition , final int bytesToWrite ) {
208
+ if (absolutePosition < 0 ) {
209
+ throw new IllegalArgumentException (format ("position must be >= 0 but was %d" , absolutePosition ));
210
+ }
211
+ if (absolutePosition > buffer .position () - bytesToWrite ) {
212
+ throw new IllegalArgumentException (format ("position must be <= %d but was %d" , buffer .position () - bytesToWrite , absolutePosition ));
213
+ }
214
+ }
165
215
}
0 commit comments