@@ -44,20 +44,24 @@ This file is part of the iText (R) project.
4444package com .itextpdf .io .source ;
4545
4646import java .lang .reflect .Method ;
47- import java .nio .Buffer ;
4847import java .nio .BufferUnderflowException ;
4948import java .security .AccessController ;
5049import java .security .PrivilegedAction ;
5150import org .slf4j .Logger ;
5251import org .slf4j .LoggerFactory ;
5352
54-
5553/**
56- * A RandomAccessSource that is based on an underlying {@link java.nio.ByteBuffer}. This class takes steps to ensure that the byte buffer
54+ * A RandomAccessSource that is based on an underlying {@link java.nio.ByteBuffer}. This class takes steps to ensure
55+ * that the byte buffer
5756 * is completely freed from memory during {@link ByteBufferRandomAccessSource#close()}
5857 */
5958class ByteBufferRandomAccessSource implements IRandomAccessSource {
6059
60+ /**
61+ * A flag to allow unmapping hack for cleaning mapped buffer
62+ */
63+ private static boolean allowUnmapping = true ;
64+
6165 /**
6266 * Internal cache of memory mapped buffers
6367 */
@@ -72,6 +76,13 @@ public ByteBufferRandomAccessSource(java.nio.ByteBuffer byteBuffer) {
7276 this .byteBuffer = byteBuffer ;
7377 }
7478
79+ /**
80+ * Enables unmapping hack
81+ */
82+ public static void disableUnmapping () {
83+ allowUnmapping = false ;
84+ }
85+
7586 /**
7687 * {@inheritDoc}
7788 * <p>
@@ -80,14 +91,15 @@ public ByteBufferRandomAccessSource(java.nio.ByteBuffer byteBuffer) {
8091 * @param position the position to read the byte from - must be less than Integer.MAX_VALUE
8192 */
8293 public int get (long position ) {
83- if (position > Integer .MAX_VALUE )
94+ if (position > Integer .MAX_VALUE ) {
8495 throw new IllegalArgumentException ("Position must be less than Integer.MAX_VALUE" );
96+ }
8597 try {
8698
87- if (position >= (( Buffer ) byteBuffer ) .limit ())
99+ if (position >= byteBuffer .limit ()) {
88100 return -1 ;
89- byte b = byteBuffer . get (( int ) position );
90- return b & 0xff ;
101+ }
102+ return byteBuffer . duplicate (). get (( int ) position ) & 0xff ;
91103 } catch (BufferUnderflowException e ) {
92104 // EOF
93105 return -1 ;
@@ -102,16 +114,18 @@ public int get(long position) {
102114 * @param position the position to read the byte from - must be less than Integer.MAX_VALUE
103115 */
104116 public int get (long position , byte [] bytes , int off , int len ) {
105- if (position > Integer .MAX_VALUE )
117+ if (position > Integer .MAX_VALUE ) {
106118 throw new IllegalArgumentException ("Position must be less than Integer.MAX_VALUE" );
119+ }
107120
108- if (position >= (( Buffer ) byteBuffer ) .limit ())
121+ if (position >= byteBuffer .limit ()) {
109122 return -1 ;
123+ }
110124
111- // Not thread safe!
112- (( Buffer ) byteBuffer ) .position ((int ) position );
113- int bytesFromThisBuffer = Math .min (len , byteBuffer .remaining ());
114- byteBuffer .get (bytes , off , bytesFromThisBuffer );
125+ final java . nio . ByteBuffer byteBufferCopy = byteBuffer . duplicate ();
126+ byteBufferCopy .position ((int ) position );
127+ final int bytesFromThisBuffer = Math .min (len , byteBufferCopy .remaining ());
128+ byteBufferCopy .get (bytes , off , bytesFromThisBuffer );
115129
116130 return bytesFromThisBuffer ;
117131 }
@@ -121,15 +135,17 @@ public int get(long position, byte[] bytes, int off, int len) {
121135 * {@inheritDoc}
122136 */
123137 public long length () {
124- return (( Buffer ) byteBuffer ) .limit ();
138+ return byteBuffer .limit ();
125139 }
126140
127141 /**
128142 * @see java.io.RandomAccessFile#close()
129143 * Cleans the mapped bytebuffers and closes the channel
130144 */
131145 public void close () throws java .io .IOException {
132- clean (byteBuffer );
146+ if (allowUnmapping ) {
147+ clean (byteBuffer );
148+ }
133149 }
134150
135151
@@ -144,11 +160,8 @@ public void close() throws java.io.IOException {
144160 private static final BufferCleaner CLEANER ;
145161
146162 static {
147- final Object hack = AccessController .doPrivileged (new PrivilegedAction <Object >() {
148- public Object run () {
149- return BufferCleaner .unmapHackImpl ();
150- }
151- });
163+ final Object hack = AccessController .doPrivileged (
164+ (PrivilegedAction <Object >) BufferCleaner ::unmapHackImpl );
152165 if (hack instanceof BufferCleaner ) {
153166 CLEANER = (BufferCleaner ) hack ;
154167 UNMAP_SUPPORTED = true ;
@@ -162,37 +175,38 @@ public Object run() {
162175 * invokes the clean method on the ByteBuffer's cleaner
163176 *
164177 * @param buffer ByteBuffer
178+ *
165179 * @return boolean true on success
166180 */
167181 private static boolean clean (final java .nio .ByteBuffer buffer ) {
168- if (buffer == null || !buffer .isDirect ())
182+ if (buffer == null || !buffer .isDirect ()) {
169183 return false ;
184+ }
170185
171- Boolean b = AccessController .doPrivileged (new PrivilegedAction <Boolean >() {
172- public Boolean run () {
173- Boolean success = Boolean .FALSE ;
174- try {
175- // java 9
176- if (UNMAP_SUPPORTED )
177- CLEANER .freeBuffer (buffer .toString (), buffer );
178- // java 8 and lower
179- else {
180- Method getCleanerMethod = buffer .getClass ().getMethod ("cleaner" , (Class <?>[]) null );
181- getCleanerMethod .setAccessible (true );
182- Object cleaner = getCleanerMethod .invoke (buffer , (Object []) null );
183- Method clean = cleaner .getClass ().getMethod ("clean" , (Class <?>[]) null );
184- clean .invoke (cleaner , (Object []) null );
185- }
186- success = Boolean .TRUE ;
187- } catch (Exception e ) {
188- // This really is a show stopper on windows
189- Logger logger = LoggerFactory .getLogger (ByteBufferRandomAccessSource .class );
190- logger .debug (e .getMessage ());
191- }
192- return success ;
193- }
194- });
186+ return AccessController .doPrivileged ((PrivilegedAction <Boolean >) () -> cleanByUnmapping (buffer ));
187+ }
195188
196- return b ;
189+ private static boolean cleanByUnmapping (final java .nio .ByteBuffer buffer ) {
190+ Boolean success = Boolean .FALSE ;
191+ try {
192+ // java 9
193+ if (UNMAP_SUPPORTED ) {
194+ CLEANER .freeBuffer (buffer .toString (), buffer );
195+ }
196+ // java 8 and lower
197+ else {
198+ Method getCleanerMethod = buffer .getClass ().getMethod ("cleaner" , (Class <?>[]) null );
199+ getCleanerMethod .setAccessible (true );
200+ Object cleaner = getCleanerMethod .invoke (buffer , (Object []) null );
201+ Method clean = cleaner .getClass ().getMethod ("clean" , (Class <?>[]) null );
202+ clean .invoke (cleaner , (Object []) null );
203+ }
204+ success = Boolean .TRUE ;
205+ } catch (Exception e ) {
206+ // This really is a show stopper on windows
207+ Logger logger = LoggerFactory .getLogger (ByteBufferRandomAccessSource .class );
208+ logger .debug (e .getMessage ());
209+ }
210+ return success ;
197211 }
198212}
0 commit comments