@@ -271,13 +271,41 @@ protected final boolean _loadMore() throws IOException
271
271
272
272
/**
273
273
* Helper method that will try to load at least specified number bytes in
274
- * input buffer, possible moving existing data around if necessary
274
+ * input buffer, possible moving existing data around if necessary.
275
+ * Exception throws if not enough content can be read.
276
+ *
277
+ * @param minAvailable Minimum number of bytes we absolutely need
278
+ *
279
+ * @throws IOException if read failed, either due to I/O issue or because not
280
+ * enough content could be read before end-of-input.
275
281
*/
276
282
protected final void _loadToHaveAtLeast (int minAvailable ) throws IOException
277
283
{
278
284
// No input stream, no leading (either we are closed, or have non-stream input source)
279
285
if (_inputStream == null ) {
280
- throw _constructError ("Needed to read " +minAvailable +" bytes, reached end-of-input" );
286
+ throw _constructError (String .format (
287
+ "Needed to read %d bytes, reached end-of-input" , minAvailable ));
288
+ }
289
+ int missing = _tryToLoadToHaveAtLeast (minAvailable );
290
+ if (missing > 0 ) {
291
+ throw _constructError (String .format (
292
+ "Needed to read %d bytes, only got %d before end-of-input" , minAvailable , minAvailable - missing ));
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Helper method that will try to load at least specified number bytes in
298
+ * input buffer, possible moving existing data around if necessary.
299
+ *
300
+ * @return Number of bytes that were missing, if any; {@code 0} for successful
301
+ * read
302
+ *
303
+ * @since 2.12.3
304
+ */
305
+ protected final int _tryToLoadToHaveAtLeast (int minAvailable ) throws IOException
306
+ {
307
+ if (_inputStream == null ) {
308
+ return minAvailable ;
281
309
}
282
310
// Need to move remaining data in front?
283
311
int amount = _inputEnd - _inputPtr ;
@@ -291,18 +319,20 @@ protected final void _loadToHaveAtLeast(int minAvailable) throws IOException
291
319
}
292
320
_inputPtr = 0 ;
293
321
while (_inputEnd < minAvailable ) {
294
- int count = _inputStream .read (_inputBuffer , _inputEnd , _inputBuffer .length - _inputEnd );
322
+ final int toRead = _inputBuffer .length - _inputEnd ;
323
+ int count = _inputStream .read (_inputBuffer , _inputEnd , toRead );
295
324
if (count < 1 ) {
296
325
// End of input
297
326
_closeInput ();
298
327
// Should never return 0, so let's fail
299
328
if (count == 0 ) {
300
329
throw new IOException ("InputStream.read() returned 0 characters when trying to read " +amount +" bytes" );
301
330
}
302
- throw _constructError ( "Needed to read " + minAvailable + " bytes, missed " + minAvailable + " before end-of-input" ) ;
331
+ return minAvailable - _inputEnd ;
303
332
}
304
333
_inputEnd += count ;
305
334
}
335
+ return 0 ;
306
336
}
307
337
308
338
@ SuppressWarnings ("deprecation" )
@@ -2459,7 +2489,7 @@ private final byte[] _finishBinaryRaw() throws IOException
2459
2489
2460
2490
if (_inputPtr >= _inputEnd ) {
2461
2491
if (!_loadMore ()) {
2462
- _reportIncompleteBinaryRead (expLen , 0 );
2492
+ _reportIncompleteBinaryReadRaw (expLen , 0 );
2463
2493
}
2464
2494
_loadMoreGuaranteed ();
2465
2495
}
@@ -2474,7 +2504,7 @@ private final byte[] _finishBinaryRaw() throws IOException
2474
2504
return b ;
2475
2505
}
2476
2506
if (!_loadMore ()) {
2477
- _reportIncompleteBinaryRead (expLen , ptr );
2507
+ _reportIncompleteBinaryReadRaw (expLen , ptr );
2478
2508
}
2479
2509
}
2480
2510
}
@@ -2492,7 +2522,7 @@ protected byte[] _finishBinaryRawLong(final int expLen) throws IOException
2492
2522
int avail = _inputEnd - _inputPtr ;
2493
2523
if (avail <= 0 ) {
2494
2524
if (!_loadMore ()) {
2495
- _reportIncompleteBinaryRead (expLen , expLen -left );
2525
+ _reportIncompleteBinaryReadRaw (expLen , expLen -left );
2496
2526
}
2497
2527
avail = _inputEnd - _inputPtr ;
2498
2528
}
@@ -2510,12 +2540,12 @@ protected byte[] _finishBinaryRawLong(final int expLen) throws IOException
2510
2540
// followed by encoded data
2511
2541
private final byte [] _read7BitBinaryWithLength () throws IOException
2512
2542
{
2513
- int byteLen = _readUnsignedVInt ();
2543
+ final int byteLen = _readUnsignedVInt ();
2514
2544
2515
2545
// 20-Mar-2021, tatu [dataformats-binary#260]: avoid eager allocation
2516
2546
// for very large content
2517
2547
if (byteLen > LONGEST_NON_CHUNKED_BINARY ) {
2518
- // return _finishBinary7BitLong(byteLen);
2548
+ return _finishBinary7BitLong (byteLen );
2519
2549
}
2520
2550
2521
2551
final byte [] result = new byte [byteLen ];
@@ -2525,7 +2555,10 @@ private final byte[] _read7BitBinaryWithLength() throws IOException
2525
2555
// first, read all 7-by-8 byte chunks
2526
2556
while (ptr <= lastOkPtr ) {
2527
2557
if ((_inputEnd - _inputPtr ) < 8 ) {
2528
- _loadToHaveAtLeast (8 );
2558
+ int missing = _tryToLoadToHaveAtLeast (8 );
2559
+ if (missing > 0 ) {
2560
+ _reportIncompleteBinaryRead7Bit (byteLen , ptr );
2561
+ }
2529
2562
}
2530
2563
int i1 = (_inputBuffer [_inputPtr ++] << 25 )
2531
2564
+ (_inputBuffer [_inputPtr ++] << 18 )
@@ -2550,7 +2583,11 @@ private final byte[] _read7BitBinaryWithLength() throws IOException
2550
2583
int toDecode = (result .length - ptr );
2551
2584
if (toDecode > 0 ) {
2552
2585
if ((_inputEnd - _inputPtr ) < (toDecode +1 )) {
2553
- _loadToHaveAtLeast (toDecode +1 );
2586
+ int missing = _tryToLoadToHaveAtLeast (toDecode +1 );
2587
+ if (missing > 0 ) {
2588
+ _reportIncompleteBinaryRead7Bit (byteLen , ptr );
2589
+ }
2590
+
2554
2591
}
2555
2592
int value = _inputBuffer [_inputPtr ++];
2556
2593
for (int i = 1 ; i < toDecode ; ++i ) {
@@ -2567,7 +2604,66 @@ private final byte[] _read7BitBinaryWithLength() throws IOException
2567
2604
// @since 2.12.3
2568
2605
protected byte [] _finishBinary7BitLong (final int expLen ) throws IOException
2569
2606
{
2570
- return null ;
2607
+ // No need to try to use recycled instance since we have much longer content
2608
+ // and there is likely less benefit of trying to recycle segments
2609
+ try (final ByteArrayBuilder bb = new ByteArrayBuilder (LONGEST_NON_CHUNKED_BINARY >> 1 )) {
2610
+ // Decode 1k input chunk at a time
2611
+ final byte [] buffer = new byte [7 * 128 ];
2612
+ int left = expLen ;
2613
+ int bufPtr = 0 ;
2614
+
2615
+ // Main loop for full 7/8 units:
2616
+ while (left >= 7 ) {
2617
+ if ((_inputEnd - _inputPtr ) < 8 ) {
2618
+ int missing = _tryToLoadToHaveAtLeast (8 );
2619
+ if (missing > 0 ) {
2620
+ _reportIncompleteBinaryRead7Bit (expLen , bb .size () + bufPtr );
2621
+ }
2622
+ }
2623
+ int i1 = (_inputBuffer [_inputPtr ++] << 25 )
2624
+ + (_inputBuffer [_inputPtr ++] << 18 )
2625
+ + (_inputBuffer [_inputPtr ++] << 11 )
2626
+ + (_inputBuffer [_inputPtr ++] << 4 );
2627
+ int x = _inputBuffer [_inputPtr ++];
2628
+ i1 += x >> 3 ;
2629
+ int i2 = ((x & 0x7 ) << 21 )
2630
+ + (_inputBuffer [_inputPtr ++] << 14 )
2631
+ + (_inputBuffer [_inputPtr ++] << 7 )
2632
+ + _inputBuffer [_inputPtr ++];
2633
+ // Ok: got our 7 bytes, just need to split, copy
2634
+ buffer [bufPtr ++] = (byte )(i1 >> 24 );
2635
+ buffer [bufPtr ++] = (byte )(i1 >> 16 );
2636
+ buffer [bufPtr ++] = (byte )(i1 >> 8 );
2637
+ buffer [bufPtr ++] = (byte )i1 ;
2638
+ buffer [bufPtr ++] = (byte )(i2 >> 16 );
2639
+ buffer [bufPtr ++] = (byte )(i2 >> 8 );
2640
+ buffer [bufPtr ++] = (byte )i2 ;
2641
+ if (bufPtr >= buffer .length ) {
2642
+ bb .write (buffer , 0 , bufPtr );
2643
+ bufPtr = 0 ;
2644
+ }
2645
+ }
2646
+
2647
+ // And then the last one; we know there is room in buffer so:
2648
+ // and then leftovers: n+1 bytes to decode n bytes
2649
+ if (left > 0 ) {
2650
+ if ((_inputEnd - _inputPtr ) < (left +1 )) {
2651
+ _loadToHaveAtLeast (left +1 );
2652
+ }
2653
+ int value = _inputBuffer [_inputPtr ++];
2654
+ for (int i = 1 ; i < left ; ++i ) {
2655
+ value = (value << 7 ) + _inputBuffer [_inputPtr ++];
2656
+ buffer [bufPtr ++] = (byte ) (value >> (7 - i ));
2657
+ }
2658
+ // last byte is different, has remaining 1 - 6 bits, right-aligned
2659
+ value <<= left ;
2660
+ buffer [bufPtr ] = (byte ) (value + _inputBuffer [_inputPtr ++]);
2661
+ }
2662
+ if (bufPtr > 0 ) {
2663
+ bb .write (buffer , 0 , bufPtr );
2664
+ }
2665
+ return bb .toByteArray ();
2666
+ }
2571
2667
}
2572
2668
2573
2669
/*
@@ -2840,12 +2936,24 @@ protected void _reportInvalidOther(int mask, int ptr) throws JsonParseException
2840
2936
}
2841
2937
2842
2938
// @since 2.12.3
2843
- protected void _reportIncompleteBinaryRead (int expLen , int actLen ) throws IOException
2939
+ protected void _reportIncompleteBinaryReadRaw (int expLen , int actLen ) throws IOException
2844
2940
{
2845
- _reportInvalidEOF (String .format (" for Binary value: expected %d bytes, only found %d" ,
2941
+ _reportInvalidEOF (String .format (
2942
+ " for Binary value (raw): expected %d bytes, only found %d" ,
2846
2943
expLen , actLen ), currentToken ());
2847
2944
}
2848
2945
2946
+ // @since 2.12.3
2947
+ protected void _reportIncompleteBinaryRead7Bit (int expLen , int actLen )
2948
+ throws IOException
2949
+ {
2950
+ // Calculate number of bytes needed (1 encoded byte expresses 7 payload bits):
2951
+ final long encodedLen = (7L + 8L * expLen ) / 7L ;
2952
+ _reportInvalidEOF (String .format (
2953
+ " for Binary value (7-bit): expected %d payload bytes (from %d encoded), only decoded %d" ,
2954
+ expLen , encodedLen , actLen ), currentToken ());
2955
+ }
2956
+
2849
2957
/*
2850
2958
/**********************************************************
2851
2959
/* Internal methods, other
0 commit comments