GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
MappedByteBufferOutputStream.java
Go to the documentation of this file.
1/**
2 * Copyright 2014 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.common.nio;
29
30import java.io.IOException;
31import java.io.OutputStream;
32import java.nio.ByteBuffer;
33import java.nio.channels.FileChannel;
34import java.nio.channels.FileChannel.MapMode;
35
36import com.jogamp.common.nio.MappedByteBufferInputStream.CacheMode;
37import com.jogamp.common.nio.MappedByteBufferInputStream.FileResizeOp;
38
39/**
40 * An {@link OutputStream} implementation based on an underlying {@link FileChannel}'s memory mapped {@link ByteBuffer}.
41 * <p>
42 * Implementation is based on {@link MappedByteBufferInputStream}, using it as its parent instance.
43 * </p>
44 * <p>
45 * An instance maybe created via its parent {@link MappedByteBufferInputStream#getOutputStream(FileResizeOp)}
46 * or directly {@link #MappedByteBufferOutputStream(FileChannel, MapMode, CacheMode, int, FileResizeOp)}.
47 * </p>
48 * @since 2.3.0
49 */
50public class MappedByteBufferOutputStream extends OutputStream {
51 private final MappedByteBufferInputStream parent;
52
54 final FileResizeOp fileResizeOp) throws IOException {
55 if( FileChannel.MapMode.READ_ONLY == parent.getMapMode() ) {
56 throw new IOException("FileChannel map-mode is read-only");
57 }
58 this.parent = parent;
59 this.parent.setFileResizeOp(fileResizeOp);
60 }
61
62 /**
63 * Creates a new instance using the given {@link FileChannel}.
64 * <p>
65 * The {@link ByteBuffer} slices will be mapped lazily at first usage.
66 * </p>
67 * @param fileChannel the file channel to be mapped lazily.
68 * @param mmode the map mode, default is {@link FileChannel.MapMode#READ_WRITE}.
69 * @param cmode the caching mode, default is {@link MappedByteBufferInputStream.CacheMode#FLUSH_PRE_SOFT}.
70 * @param sliceShift the pow2 slice size, default is {@link MappedByteBufferInputStream#DEFAULT_SLICE_SHIFT}.
71 * @param fileResizeOp {@link MappedByteBufferInputStream.FileResizeOp} as described on {@link MappedByteBufferInputStream#setFileResizeOp(FileResizeOp)}.
72 * @throws IOException
73 */
74 public MappedByteBufferOutputStream(final FileChannel fileChannel,
75 final FileChannel.MapMode mmode,
76 final CacheMode cmode,
77 final int sliceShift, final FileResizeOp fileResizeOp) throws IOException {
78 this(new MappedByteBufferInputStream(fileChannel, mmode, cmode, sliceShift, fileChannel.size(), 0), fileResizeOp);
79 }
80
81 /**
82 * See {@link MappedByteBufferInputStream#setSynchronous(boolean)}.
83 */
84 public final synchronized void setSynchronous(final boolean s) {
85 parent.setSynchronous(s);
86 }
87 /**
88 * See {@link MappedByteBufferInputStream#getSynchronous()}.
89 */
90 public final synchronized boolean getSynchronous() {
91 return parent.getSynchronous();
92 }
93
94 /**
95 * See {@link MappedByteBufferInputStream#setLength(long)}.
96 */
97 public final synchronized void setLength(final long newTotalSize) throws IOException {
98 parent.setLength(newTotalSize);
99 }
100
101 /**
102 * See {@link MappedByteBufferInputStream#notifyLengthChange(long)}.
103 */
104 public final synchronized void notifyLengthChange(final long newTotalSize) throws IOException {
105 parent.notifyLengthChange(newTotalSize);
106 }
107
108 /**
109 * See {@link MappedByteBufferInputStream#length()}.
110 */
111 public final synchronized long length() {
112 return parent.length();
113 }
114
115 /**
116 * See {@link MappedByteBufferInputStream#remaining()}.
117 */
118 public final synchronized long remaining() throws IOException {
119 return parent.remaining();
120 }
121
122 /**
123 * See {@link MappedByteBufferInputStream#position()}.
124 */
125 public final synchronized long position() throws IOException {
126 return parent.position();
127 }
128
129 /**
130 * See {@link MappedByteBufferInputStream#position(long)}.
131 */
132 public final synchronized MappedByteBufferInputStream position( final long newPosition ) throws IOException {
133 return parent.position(newPosition);
134 }
135
136 /**
137 * See {@link MappedByteBufferInputStream#skip(long)}.
138 */
139 public final synchronized long skip( final long n ) throws IOException {
140 return parent.skip(n);
141 }
142
143 @Override
144 public final synchronized void flush() throws IOException {
145 parent.flush( true /* metaData */);
146 }
147
148 /**
149 * See {@link MappedByteBufferInputStream#flush(boolean)}.
150 */
151 // @Override
152 public final synchronized void flush(final boolean metaData) throws IOException {
153 parent.flush(metaData);
154 }
155
156 @Override
157 public final synchronized void close() throws IOException {
158 parent.close();
159 }
160
161 @Override
162 public final synchronized void write(final int b) throws IOException {
163 parent.checkOpen();
164 final long totalRem = parent.remaining();
165 if ( totalRem < 1 ) { // grow if required
166 parent.setLength( parent.length() + 1 );
167 }
168 ByteBuffer slice = parent.currentSlice();
169 final int currRem = slice.remaining();
170 if ( 0 == currRem ) {
171 if ( null == ( slice = parent.nextSlice() ) ) {
172 if( MappedByteBufferInputStream.DEBUG ) {
173 System.err.println("EOT write: "+parent.currentSlice());
174 parent.dbgDump("EOT write:", System.err);
175 }
176 throw new IOException("EOT"); // 'end-of-tape'
177 }
178 }
179 slice.put( (byte)(b & 0xFF) );
180
181 // sync last buffer (happens only in synchronous mode)
182 if( null != slice ) {
183 parent.syncSlice(slice);
184 }
185 }
186
187 @Override
188 public final synchronized void write(final byte b[], final int off, final int len) throws IOException {
189 parent.checkOpen();
190 if (b == null) {
191 throw new NullPointerException();
192 } else if( off < 0 ||
193 len < 0 ||
194 off > b.length ||
195 off + len > b.length ||
196 off + len < 0
197 ) {
198 throw new IndexOutOfBoundsException("offset "+off+", length "+len+", b.length "+b.length);
199 } else if( 0 == len ) {
200 return;
201 }
202 final long totalRem = parent.remaining();
203 if ( totalRem < len ) { // grow if required
204 parent.setLength( parent.length() + len - totalRem );
205 }
206 int written = 0;
207 ByteBuffer slice = null;
208 while( written < len ) {
209 slice = parent.currentSlice();
210 int currRem = slice.remaining();
211 if ( 0 == currRem ) {
212 if ( null == ( slice = parent.nextSlice() ) ) {
213 if( MappedByteBufferInputStream.DEBUG ) {
214 System.err.println("EOT write: offset "+off+", length "+len+", b.length "+b.length);
215 System.err.println("EOT write: written "+written+" / "+len+", currRem "+currRem);
216 System.err.println("EOT write: "+parent.currentSlice());
217 parent.dbgDump("EOT write:", System.err);
218 }
219 throw new InternalError("EOT"); // 'end-of-tape'
220 }
221 currRem = slice.remaining();
222 }
223 final int currLen = Math.min( len - written, currRem );
224 slice.put( b, off + written, currLen );
225 written += currLen;
226 }
227 // sync last buffer (happens only in synchronous mode)
228 if( null != slice ) {
229 parent.syncSlice(slice);
230 }
231 }
232
233 /**
234 * Perform similar to {@link #write(byte[], int, int)}
235 * with {@link ByteBuffer} instead of byte array.
236 * @param b the {@link ByteBuffer} source, data is read from current {@link ByteBuffer#position()}
237 * @param len the number of bytes to write
238 * @throws IOException if a buffer slice operation failed or stream has been {@link #close() closed}.
239 */
240 // @Override
241 public final synchronized void write(final ByteBuffer b, final int len) throws IOException {
242 parent.checkOpen();
243 if (b == null) {
244 throw new NullPointerException();
245 } else if (len < 0 || len > b.remaining()) {
246 throw new IndexOutOfBoundsException("length "+len+", b "+b);
247 } else if( 0 == len ) {
248 return;
249 }
250 final long totalRem = parent.remaining();
251 if ( totalRem < len ) { // grow if required
252 parent.setLength( parent.length() + len - totalRem );
253 }
254 int written = 0;
255 ByteBuffer slice = null;
256 while( written < len ) {
257 slice = parent.currentSlice();
258 int currRem = slice.remaining();
259 if ( 0 == currRem ) {
260 if ( null == ( slice = parent.nextSlice() ) ) {
261 if( MappedByteBufferInputStream.DEBUG ) {
262 System.err.println("EOT write: length "+len+", b "+b);
263 System.err.println("EOT write: written "+written+" / "+len+", currRem "+currRem);
264 System.err.println("EOT write: "+parent.currentSlice());
265 parent.dbgDump("EOT write:", System.err);
266 }
267 throw new InternalError("EOT"); // 'end-of-tape'
268 }
269 currRem = slice.remaining();
270 }
271 final int currLen = Math.min( len - written, currRem );
272
273 if( slice.hasArray() && b.hasArray() ) {
274 System.arraycopy(b.array(), b.arrayOffset() + b.position(),
275 slice.array(), slice.arrayOffset() + slice.position(),
276 currLen);
277 b.position( b.position() + currLen );
278 slice.position( slice.position() + currLen );
279 } else if( currLen == currRem ) {
280 slice.put(b);
281 } else {
282 final int _limit = b.limit();
283 b.limit(currLen);
284 try {
285 slice.put(b);
286 } finally {
287 b.limit(_limit);
288 }
289 }
290 written += currLen;
291 }
292 // sync last buffer (happens only in synchronous mode)
293 if( null != slice ) {
294 parent.syncSlice(slice);
295 }
296 }
297
298 /**
299 * Perform similar to {@link #write(ByteBuffer, int)}
300 * with {@link MappedByteBufferInputStream} instead of byte array.
301 * <p>
302 * Method directly copies memory mapped {@link ByteBuffer}'ed data
303 * from the given input stream to this stream without extra data copy.
304 * </p>
305 * @param b the {@link ByteBuffer} source, data is read from current {@link MappedByteBufferInputStream#position()}
306 * @param len the number of bytes to write
307 * @throws IOException if a buffer slice operation failed or stream has been {@link #close() closed}.
308 */
309 // @Override
310 public final synchronized void write(final MappedByteBufferInputStream b, final long len) throws IOException {
311 parent.checkOpen();
312 if (b == null) {
313 throw new NullPointerException();
314 } else if (len < 0 || len > b.remaining()) {
315 throw new IndexOutOfBoundsException("length "+len+", b "+b);
316 } else if( 0 == len ) {
317 return;
318 }
319 final long totalRem = parent.remaining();
320 if ( totalRem < len ) { // grow if required
321 parent.setLength( parent.length() + len - totalRem );
322 }
323 long written = 0;
324 ByteBuffer slice = null;
325 while( written < len ) {
326 slice = parent.currentSlice();
327 int currRem = slice.remaining();
328 if ( 0 == currRem ) {
329 if ( null == ( slice = parent.nextSlice() ) ) {
330 if( MappedByteBufferInputStream.DEBUG ) {
331 System.err.println("EOT write: length "+len+", b "+b);
332 System.err.println("EOT write: written "+written+" / "+len+", currRem "+currRem);
333 System.err.println("EOT write: "+parent.currentSlice());
334 parent.dbgDump("EOT write:", System.err);
335 }
336 throw new InternalError("EOT"); // 'end-of-tape'
337 }
338 currRem = slice.remaining();
339 }
340 final int currLen = b.read(slice, (int)Math.min( len - written, currRem ));
341 if( 0 > currLen ) {
342 throw new InternalError("Unexpected InputStream EOT"); // 'end-of-tape'
343 }
344 written += currLen;
345 }
346 // sync last buffer (happens only in synchronous mode)
347 if( null != slice ) {
348 parent.syncSlice(slice);
349 }
350 }
351}
An InputStream implementation based on an underlying FileChannel's memory mapped ByteBuffer,...
final synchronized void flush(final boolean metaData)
Similar to OutputStream#flush(), synchronizes all mapped buffers from local storage via MappedByteBuf...
final synchronized void setFileResizeOp(final FileResizeOp fileResizeOp)
final synchronized long length()
Returns the total size in bytes of the InputStream.
final synchronized long position()
Returns the absolute position of the InputStream.
final synchronized void setLength(final long newTotalSize)
Resize the underlying FileChannel's size and adjusting this instance via accordingly.
final synchronized long remaining()
Returns the number of remaining available bytes of the InputStream, i.e.
final synchronized ByteBuffer currentSlice()
Return the mapped ByteBuffer slice at the current position().
final synchronized ByteBuffer nextSlice()
Return the next mapped ByteBuffer slice from the current position(), implicitly setting position(long...
final synchronized void setSynchronous(final boolean s)
Enable or disable synchronous mode.
final synchronized void notifyLengthChange(final long newTotalSize)
Notify this instance that the underlying FileChannel's size has been changed and adjusting this insta...
final synchronized boolean getSynchronous()
Return synchronous mode.
An OutputStream implementation based on an underlying FileChannel's memory mapped ByteBuffer.
final synchronized void setLength(final long newTotalSize)
See MappedByteBufferInputStream#setLength(long).
final synchronized long remaining()
See MappedByteBufferInputStream#remaining().
final synchronized MappedByteBufferInputStream position(final long newPosition)
See MappedByteBufferInputStream#position(long).
final synchronized long length()
See MappedByteBufferInputStream#length().
final synchronized boolean getSynchronous()
See MappedByteBufferInputStream#getSynchronous().
final synchronized void flush(final boolean metaData)
See MappedByteBufferInputStream#flush(boolean).
MappedByteBufferOutputStream(final FileChannel fileChannel, final FileChannel.MapMode mmode, final CacheMode cmode, final int sliceShift, final FileResizeOp fileResizeOp)
Creates a new instance using the given FileChannel.
final synchronized void write(final MappedByteBufferInputStream b, final long len)
Perform similar to write(ByteBuffer, int) with MappedByteBufferInputStream instead of byte array.
final synchronized void write(final byte b[], final int off, final int len)
final synchronized void write(final ByteBuffer b, final int len)
Perform similar to write(byte[], int, int) with ByteBuffer instead of byte array.
final synchronized long skip(final long n)
See MappedByteBufferInputStream#skip(long).
final synchronized long position()
See MappedByteBufferInputStream#position().
final synchronized void setSynchronous(final boolean s)
See MappedByteBufferInputStream#setSynchronous(boolean).
final synchronized void notifyLengthChange(final long newTotalSize)
See MappedByteBufferInputStream#notifyLengthChange(long).
File resize interface allowing a file to change its size, e.g.