1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.allanbank.mongodb.bson.io;
21
22 import java.io.IOException;
23 import java.io.OutputStream;
24 import java.nio.charset.Charset;
25 import java.util.ArrayList;
26 import java.util.List;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 public class RandomAccessOutputStream extends OutputStream {
44
45 public final static Charset UTF8 = StringDecoder.UTF8;
46
47
48 private static final int BUFFER_SIZE;
49
50
51 private static final int BUFFER_SIZE_MASK;
52
53
54 private static final int BUFFER_SIZE_SHIFT;
55
56 static {
57 BUFFER_SIZE = Integer.highestOneBit(8192);
58 BUFFER_SIZE_MASK = BUFFER_SIZE - 1;
59 BUFFER_SIZE_SHIFT = Integer.numberOfTrailingZeros(BUFFER_SIZE);
60 }
61
62
63 private final List<byte[]> myBuffers;
64
65
66 private byte[] myCurrentBuffer;
67
68
69 private int myCurrentBufferIndex;
70
71
72 private int myCurrentBufferOffset;
73
74
75
76
77
78
79
80
81
82 private final byte[] myIntegerBytes;
83
84
85 private long mySize;
86
87
88 private final StringEncoder myStringEncoder;
89
90
91
92
93 public RandomAccessOutputStream() {
94 this(new StringEncoderCache());
95 }
96
97
98
99
100
101
102
103 public RandomAccessOutputStream(final StringEncoderCache cache) {
104 mySize = 0;
105 myCurrentBufferOffset = 0;
106 myCurrentBufferIndex = 0;
107 myCurrentBuffer = new byte[BUFFER_SIZE];
108
109 myStringEncoder = new StringEncoder(cache);
110
111 myBuffers = new ArrayList<byte[]>();
112 myBuffers.add(myCurrentBuffer);
113
114 myIntegerBytes = new byte[8];
115 }
116
117
118
119
120 @Override
121 public void close() {
122
123 }
124
125
126
127
128 @Override
129 public void flush() {
130
131 }
132
133
134
135
136
137
138
139
140
141
142
143 @Deprecated
144 public int getMaxCachedStringEntries() {
145 return myStringEncoder.getCache().getMaxCacheEntries();
146 }
147
148
149
150
151
152
153
154
155
156
157
158 @Deprecated
159 public int getMaxCachedStringLength() {
160 return myStringEncoder.getCache().getMaxCacheLength();
161 }
162
163
164
165
166
167
168
169 public long getPosition() {
170 return getSize();
171 }
172
173
174
175
176
177
178 public long getSize() {
179 return mySize;
180 }
181
182
183
184
185
186 public void reset() {
187 mySize = 0;
188 myCurrentBufferOffset = 0;
189 myCurrentBufferIndex = 0;
190 myCurrentBuffer = myBuffers.get(0);
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204 @Deprecated
205 public void setMaxCachedStringEntries(final int maxCacheEntries) {
206 myStringEncoder.getCache().setMaxCacheEntries(maxCacheEntries);
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220
221 @Deprecated
222 public void setMaxCachedStringLength(final int maxlength) {
223 myStringEncoder.getCache().setMaxCacheLength(maxlength);
224
225 }
226
227
228
229
230
231
232
233 @Override
234 public void write(final byte buffer[]) {
235 write(buffer, 0, buffer.length);
236 }
237
238
239
240
241
242
243
244
245
246
247
248 @Override
249 public void write(final byte buffer[], final int offset, final int length) {
250 if (buffer == null) {
251 throw new NullPointerException();
252 }
253 else if ((offset < 0) || (offset > buffer.length) || (length < 0)
254 || ((offset + length) > buffer.length)
255 || ((offset + length) < 0)) {
256 throw new IndexOutOfBoundsException();
257 }
258 else if (length == 0) {
259 return;
260 }
261
262 int wrote = 0;
263 while (wrote < length) {
264 if (myCurrentBuffer.length <= myCurrentBufferOffset) {
265 nextBuffer();
266 }
267
268 final int available = myCurrentBuffer.length
269 - myCurrentBufferOffset;
270 final int toWrite = Math.min(length - wrote, available);
271
272 System.arraycopy(buffer, offset + wrote, myCurrentBuffer,
273 myCurrentBufferOffset, toWrite);
274
275 myCurrentBufferOffset += toWrite;
276 mySize += toWrite;
277 wrote += toWrite;
278 }
279 }
280
281
282
283
284 @Override
285 public void write(final int b) {
286 if (myCurrentBuffer.length <= myCurrentBufferOffset) {
287 nextBuffer();
288 }
289
290 myCurrentBuffer[myCurrentBufferOffset] = (byte) b;
291 myCurrentBufferOffset += 1;
292 mySize += 1;
293 }
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308 public void writeAt(final long position, final byte buffer[]) {
309 writeAt(position, buffer, 0, buffer.length);
310 }
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326 public void writeAt(final long position, final byte buffer[],
327 final int offset, final int length) {
328 if (buffer == null) {
329 throw new NullPointerException();
330 }
331 else if ((offset < 0) || (offset > buffer.length) || (length < 0)
332 || ((offset + length) > buffer.length)
333 || ((offset + length) < 0) || ((position + length) > getSize())) {
334 throw new IndexOutOfBoundsException();
335 }
336 else if (length == 0) {
337 return;
338 }
339
340
341 final long start = position & BUFFER_SIZE_MASK;
342 int bufferIndex = (int) (position >> BUFFER_SIZE_SHIFT);
343 byte[] internalBuffer = myBuffers.get(bufferIndex);
344
345
346 int wrote = 0;
347 int internalOffset = (int) start;
348 while (wrote < length) {
349 if (internalBuffer.length <= internalOffset) {
350 bufferIndex += 1;
351 internalBuffer = myBuffers.get(bufferIndex);
352 internalOffset = 0;
353 }
354
355 final int available = internalBuffer.length - internalOffset;
356 final int toWrite = Math.min(length - wrote, available);
357
358 System.arraycopy(buffer, offset + wrote, internalBuffer,
359 internalOffset, toWrite);
360
361 internalOffset += toWrite;
362 wrote += toWrite;
363 }
364 }
365
366
367
368
369
370
371
372
373
374
375
376 public void writeAt(final long position, final int b) {
377
378 final long start = position & BUFFER_SIZE_MASK;
379 final int bufferIndex = (int) (position >> BUFFER_SIZE_SHIFT);
380 final byte[] internalBuffer = myBuffers.get(bufferIndex);
381
382 internalBuffer[(int) start] = (byte) b;
383 }
384
385
386
387
388
389
390
391 public void writeByte(final byte b) {
392 write(b & 0xFF);
393 }
394
395
396
397
398
399
400
401 public void writeBytes(final byte[] data) {
402 write(data);
403 }
404
405
406
407
408
409
410
411
412 public void writeCString(final String... strings) {
413 for (final String string : strings) {
414
415 try {
416 myStringEncoder.encode(string, this);
417 }
418 catch (final IOException cannotHappen) {
419
420 throw new IllegalStateException(
421 "Encoder should not throw when writing to a buffer.");
422 }
423 }
424 writeByte((byte) 0);
425 }
426
427
428
429
430
431
432
433 public void writeInt(final int value) {
434 myIntegerBytes[0] = (byte) (value & 0xFF);
435 myIntegerBytes[1] = (byte) ((value >> 8) & 0xFF);
436 myIntegerBytes[2] = (byte) ((value >> 16) & 0xFF);
437 myIntegerBytes[3] = (byte) ((value >> 24) & 0xFF);
438
439 write(myIntegerBytes, 0, 4);
440 }
441
442
443
444
445
446
447
448
449
450
451 public void writeIntAt(final long position, final int value) {
452 myIntegerBytes[0] = (byte) (value & 0xFF);
453 myIntegerBytes[1] = (byte) ((value >> 8) & 0xFF);
454 myIntegerBytes[2] = (byte) ((value >> 16) & 0xFF);
455 myIntegerBytes[3] = (byte) ((value >> 24) & 0xFF);
456
457 writeAt(position, myIntegerBytes, 0, 4);
458 }
459
460
461
462
463
464
465
466 public void writeLong(final long value) {
467 myIntegerBytes[0] = (byte) (value & 0xFF);
468 myIntegerBytes[1] = (byte) ((value >> 8) & 0xFF);
469 myIntegerBytes[2] = (byte) ((value >> 16) & 0xFF);
470 myIntegerBytes[3] = (byte) ((value >> 24) & 0xFF);
471 myIntegerBytes[4] = (byte) ((value >> 32) & 0xFF);
472 myIntegerBytes[5] = (byte) ((value >> 40) & 0xFF);
473 myIntegerBytes[6] = (byte) ((value >> 48) & 0xFF);
474 myIntegerBytes[7] = (byte) ((value >> 56) & 0xFF);
475
476 write(myIntegerBytes, 0, 8);
477 }
478
479
480
481
482
483
484
485 public void writeString(final String string) {
486
487
488
489
490
491
492 final long position = getPosition();
493 writeInt(0);
494 try {
495 myStringEncoder.encode(string, this);
496 }
497 catch (final IOException cannotHappen) {
498
499 throw new IllegalStateException(
500 "Encoder should not throw when writing to a buffer.");
501 }
502 writeByte((byte) 0);
503
504 final int size = (int) (getPosition() - position - 4);
505 writeIntAt(position, size);
506
507 }
508
509
510
511
512
513
514
515
516
517
518
519 public void writeTo(final OutputStream out) throws IOException {
520 for (int i = 0; i < myCurrentBufferIndex; ++i) {
521 out.write(myBuffers.get(i), 0, BUFFER_SIZE);
522 }
523 out.write(myCurrentBuffer, 0, myCurrentBufferOffset);
524 }
525
526
527
528
529 protected void nextBuffer() {
530
531 myCurrentBufferIndex += 1;
532
533 if (myCurrentBufferIndex < myBuffers.size()) {
534 myCurrentBuffer = myBuffers.get(myCurrentBufferIndex);
535 }
536 else {
537 myCurrentBuffer = new byte[BUFFER_SIZE];
538 myBuffers.add(myCurrentBuffer);
539 }
540
541 myCurrentBufferOffset = 0;
542 }
543 }