jaulib v1.3.0
Jau Support Library (C++, Java, ..)
MappedByteBufferOutputStream.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 Gothel Software e.K.
4 * Copyright (c) 2014 Gothel Software e.K.
5 * Copyright (c) 2014 JogAmp Community.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26package org.jau.io;
27
28import java.io.IOException;
29import java.io.OutputStream;
30import java.nio.ByteBuffer;
31import java.nio.channels.FileChannel;
32import java.nio.channels.FileChannel.MapMode;
33
34import org.jau.io.MappedByteBufferInputStream.CacheMode;
35import org.jau.io.MappedByteBufferInputStream.FileResizeOp;
36
37/**
38 * An {@link OutputStream} implementation based on an underlying {@link FileChannel}'s memory mapped {@link ByteBuffer}.
39 * <p>
40 * Implementation is based on {@link MappedByteBufferInputStream}, using it as its parent instance.
41 * </p>
42 * <p>
43 * An instance maybe created via its parent {@link MappedByteBufferInputStream#getOutputStream(FileResizeOp)}
44 * or directly {@link #MappedByteBufferOutputStream(FileChannel, MapMode, CacheMode, int, FileResizeOp)}.
45 * </p>
46 * @since 0.3.0
47 */
48public class MappedByteBufferOutputStream extends OutputStream {
49 private final MappedByteBufferInputStream parent;
50
52 final FileResizeOp fileResizeOp) throws IOException {
53 if( FileChannel.MapMode.READ_ONLY == parent.getMapMode() ) {
54 throw new IOException("FileChannel map-mode is read-only");
55 }
56 this.parent = parent;
57 this.parent.setFileResizeOp(fileResizeOp);
58 }
59
60 /**
61 * Creates a new instance using the given {@link FileChannel}.
62 * <p>
63 * The {@link ByteBuffer} slices will be mapped lazily at first usage.
64 * </p>
65 * @param fileChannel the file channel to be mapped lazily.
66 * @param mmode the map mode, default is {@link FileChannel.MapMode#READ_WRITE}.
67 * @param cmode the caching mode, default is {@link MappedByteBufferInputStream.CacheMode#FLUSH_PRE_SOFT}.
68 * @param sliceShift the pow2 slice size, default is {@link MappedByteBufferInputStream#DEFAULT_SLICE_SHIFT}.
69 * @param fileResizeOp {@link MappedByteBufferInputStream.FileResizeOp} as described on {@link MappedByteBufferInputStream#setFileResizeOp(FileResizeOp)}.
70 * @throws IOException
71 */
72 public MappedByteBufferOutputStream(final FileChannel fileChannel,
73 final FileChannel.MapMode mmode,
74 final CacheMode cmode,
75 final int sliceShift, final FileResizeOp fileResizeOp) throws IOException {
76 this(new MappedByteBufferInputStream(fileChannel, mmode, cmode, sliceShift, fileChannel.size(), 0), fileResizeOp);
77 }
78
79 /**
80 * See {@link MappedByteBufferInputStream#setSynchronous(boolean)}.
81 */
82 public final synchronized void setSynchronous(final boolean s) {
83 parent.setSynchronous(s);
84 }
85 /**
86 * See {@link MappedByteBufferInputStream#getSynchronous()}.
87 */
88 public final synchronized boolean getSynchronous() {
89 return parent.getSynchronous();
90 }
91
92 /**
93 * See {@link MappedByteBufferInputStream#setLength(long)}.
94 */
95 public final synchronized void setLength(final long newTotalSize) throws IOException {
96 parent.setLength(newTotalSize);
97 }
98
99 /**
100 * See {@link MappedByteBufferInputStream#notifyLengthChange(long)}.
101 */
102 public final synchronized void notifyLengthChange(final long newTotalSize) throws IOException {
103 parent.notifyLengthChange(newTotalSize);
104 }
105
106 /**
107 * See {@link MappedByteBufferInputStream#length()}.
108 */
109 public final synchronized long length() {
110 return parent.length();
111 }
112
113 /**
114 * See {@link MappedByteBufferInputStream#remaining()}.
115 */
116 public final synchronized long remaining() throws IOException {
117 return parent.remaining();
118 }
119
120 /**
121 * See {@link MappedByteBufferInputStream#position()}.
122 */
123 public final synchronized long position() throws IOException {
124 return parent.position();
125 }
126
127 /**
128 * See {@link MappedByteBufferInputStream#position(long)}.
129 */
130 public final synchronized MappedByteBufferInputStream position( final long newPosition ) throws IOException {
131 return parent.position(newPosition);
132 }
133
134 /**
135 * See {@link MappedByteBufferInputStream#skip(long)}.
136 */
137 public final synchronized long skip( final long n ) throws IOException {
138 return parent.skip(n);
139 }
140
141 @Override
142 public final synchronized void flush() throws IOException {
143 parent.flush( true /* metaData */);
144 }
145
146 /**
147 * See {@link MappedByteBufferInputStream#flush(boolean)}.
148 */
149 // @Override
150 public final synchronized void flush(final boolean metaData) throws IOException {
151 parent.flush(metaData);
152 }
153
154 @Override
155 public final synchronized void close() throws IOException {
156 parent.close();
157 }
158
159 @Override
160 public final synchronized void write(final int b) throws IOException {
161 parent.checkOpen();
162 final long totalRem = parent.remaining();
163 if ( totalRem < 1 ) { // grow if required
164 parent.setLength( parent.length() + 1 );
165 }
166 ByteBuffer slice = parent.currentSlice();
167 final int currRem = slice.remaining();
168 if ( 0 == currRem ) {
169 if ( null == ( slice = parent.nextSlice() ) ) {
170 if( MappedByteBufferInputStream.DEBUG ) {
171 System.err.println("EOT write: "+parent.currentSlice());
172 parent.dbgDump("EOT write:", System.err);
173 }
174 throw new IOException("EOT"); // 'end-of-tape'
175 }
176 }
177 slice.put( (byte)(b & 0xFF) );
178
179 // sync last buffer (happens only in synchronous mode)
180 if( null != slice ) {
181 parent.syncSlice(slice);
182 }
183 }
184
185 @Override
186 public final synchronized void write(final byte b[], final int off, final int len) throws IOException {
187 parent.checkOpen();
188 if (b == null) {
189 throw new NullPointerException();
190 } else if( off < 0 ||
191 len < 0 ||
192 off > b.length ||
193 off + len > b.length ||
194 off + len < 0
195 ) {
196 throw new IndexOutOfBoundsException("offset "+off+", length "+len+", b.length "+b.length);
197 } else if( 0 == len ) {
198 return;
199 }
200 final long totalRem = parent.remaining();
201 if ( totalRem < len ) { // grow if required
202 parent.setLength( parent.length() + len - totalRem );
203 }
204 int written = 0;
205 ByteBuffer slice = null;
206 while( written < len ) {
207 slice = parent.currentSlice();
208 int currRem = slice.remaining();
209 if ( 0 == currRem ) {
210 if ( null == ( slice = parent.nextSlice() ) ) {
211 if( MappedByteBufferInputStream.DEBUG ) {
212 System.err.println("EOT write: offset "+off+", length "+len+", b.length "+b.length);
213 System.err.println("EOT write: written "+written+" / "+len+", currRem "+currRem);
214 System.err.println("EOT write: "+parent.currentSlice());
215 parent.dbgDump("EOT write:", System.err);
216 }
217 throw new InternalError("EOT"); // 'end-of-tape'
218 }
219 currRem = slice.remaining();
220 }
221 final int currLen = Math.min( len - written, currRem );
222 slice.put( b, off + written, currLen );
223 written += currLen;
224 }
225 // sync last buffer (happens only in synchronous mode)
226 if( null != slice ) {
227 parent.syncSlice(slice);
228 }
229 }
230
231 /**
232 * Perform similar to {@link #write(byte[], int, int)}
233 * with {@link ByteBuffer} instead of byte array.
234 * @param b the {@link ByteBuffer} source, data is read from current {@link ByteBuffer#position()}
235 * @param len the number of bytes to write
236 * @throws IOException if a buffer slice operation failed or stream has been {@link #close() closed}.
237 */
238 // @Override
239 public final synchronized void write(final ByteBuffer b, final int len) throws IOException {
240 parent.checkOpen();
241 if (b == null) {
242 throw new NullPointerException();
243 } else if (len < 0 || len > b.remaining()) {
244 throw new IndexOutOfBoundsException("length "+len+", b "+b);
245 } else if( 0 == len ) {
246 return;
247 }
248 final long totalRem = parent.remaining();
249 if ( totalRem < len ) { // grow if required
250 parent.setLength( parent.length() + len - totalRem );
251 }
252 int written = 0;
253 ByteBuffer slice = null;
254 while( written < len ) {
255 slice = parent.currentSlice();
256 int currRem = slice.remaining();
257 if ( 0 == currRem ) {
258 if ( null == ( slice = parent.nextSlice() ) ) {
259 if( MappedByteBufferInputStream.DEBUG ) {
260 System.err.println("EOT write: length "+len+", b "+b);
261 System.err.println("EOT write: written "+written+" / "+len+", currRem "+currRem);
262 System.err.println("EOT write: "+parent.currentSlice());
263 parent.dbgDump("EOT write:", System.err);
264 }
265 throw new InternalError("EOT"); // 'end-of-tape'
266 }
267 currRem = slice.remaining();
268 }
269 final int currLen = Math.min( len - written, currRem );
270
271 if( slice.hasArray() && b.hasArray() ) {
272 System.arraycopy(b.array(), b.arrayOffset() + b.position(),
273 slice.array(), slice.arrayOffset() + slice.position(),
274 currLen);
275 b.position( b.position() + currLen );
276 slice.position( slice.position() + currLen );
277 } else if( currLen == currRem ) {
278 slice.put(b);
279 } else {
280 final int _limit = b.limit();
281 b.limit(currLen);
282 try {
283 slice.put(b);
284 } finally {
285 b.limit(_limit);
286 }
287 }
288 written += currLen;
289 }
290 // sync last buffer (happens only in synchronous mode)
291 if( null != slice ) {
292 parent.syncSlice(slice);
293 }
294 }
295
296 /**
297 * Perform similar to {@link #write(ByteBuffer, int)}
298 * with {@link MappedByteBufferInputStream} instead of byte array.
299 * <p>
300 * Method directly copies memory mapped {@link ByteBuffer}'ed data
301 * from the given input stream to this stream without extra data copy.
302 * </p>
303 * @param b the {@link ByteBuffer} source, data is read from current {@link MappedByteBufferInputStream#position()}
304 * @param len the number of bytes to write
305 * @throws IOException if a buffer slice operation failed or stream has been {@link #close() closed}.
306 */
307 // @Override
308 public final synchronized void write(final MappedByteBufferInputStream b, final long len) throws IOException {
309 parent.checkOpen();
310 if (b == null) {
311 throw new NullPointerException();
312 } else if (len < 0 || len > b.remaining()) {
313 throw new IndexOutOfBoundsException("length "+len+", b "+b);
314 } else if( 0 == len ) {
315 return;
316 }
317 final long totalRem = parent.remaining();
318 if ( totalRem < len ) { // grow if required
319 parent.setLength( parent.length() + len - totalRem );
320 }
321 long written = 0;
322 ByteBuffer slice = null;
323 while( written < len ) {
324 slice = parent.currentSlice();
325 int currRem = slice.remaining();
326 if ( 0 == currRem ) {
327 if ( null == ( slice = parent.nextSlice() ) ) {
328 if( MappedByteBufferInputStream.DEBUG ) {
329 System.err.println("EOT write: length "+len+", b "+b);
330 System.err.println("EOT write: written "+written+" / "+len+", currRem "+currRem);
331 System.err.println("EOT write: "+parent.currentSlice());
332 parent.dbgDump("EOT write:", System.err);
333 }
334 throw new InternalError("EOT"); // 'end-of-tape'
335 }
336 currRem = slice.remaining();
337 }
338 final int currLen = b.read(slice, (int)Math.min( len - written, currRem ));
339 if( 0 > currLen ) {
340 throw new InternalError("Unexpected InputStream EOT"); // 'end-of-tape'
341 }
342 written += currLen;
343 }
344 // sync last buffer (happens only in synchronous mode)
345 if( null != slice ) {
346 parent.syncSlice(slice);
347 }
348 }
349}
An InputStream implementation based on an underlying FileChannel's memory mapped ByteBuffer,...
final synchronized long remaining()
Returns the number of remaining available bytes of the InputStream, i.e.
final synchronized void setFileResizeOp(final FileResizeOp fileResizeOp)
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 long skip(final long n)
final synchronized ByteBuffer currentSlice()
Return the mapped ByteBuffer slice at the current position().
final synchronized long length()
Returns the total size in bytes of the InputStream.
final synchronized ByteBuffer nextSlice()
Return the next mapped ByteBuffer slice from the current position(), implicitly setting position(long...
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 boolean getSynchronous()
Return synchronous mode.
final synchronized void setSynchronous(final boolean s)
Enable or disable synchronous mode.
final synchronized void flush(final boolean metaData)
Similar to OutputStream#flush(), synchronizes all mapped buffers from local storage via MappedByteBuf...
An OutputStream implementation based on an underlying FileChannel's memory mapped ByteBuffer.
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 position()
See MappedByteBufferInputStream#position().
final synchronized long remaining()
See MappedByteBufferInputStream#remaining().
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 flush(final boolean metaData)
See MappedByteBufferInputStream#flush(boolean).
final synchronized long length()
See MappedByteBufferInputStream#length().
final synchronized boolean getSynchronous()
See MappedByteBufferInputStream#getSynchronous().
final synchronized long skip(final long n)
See MappedByteBufferInputStream#skip(long).
final synchronized void notifyLengthChange(final long newTotalSize)
See MappedByteBufferInputStream#notifyLengthChange(long).
final synchronized void setLength(final long newTotalSize)
See MappedByteBufferInputStream#setLength(long).
final synchronized void write(final byte b[], final int off, final int len)
final synchronized MappedByteBufferInputStream position(final long newPosition)
See MappedByteBufferInputStream#position(long).
final synchronized void setSynchronous(final boolean s)
See MappedByteBufferInputStream#setSynchronous(boolean).
final synchronized void write(final MappedByteBufferInputStream b, final long len)
Perform similar to write(ByteBuffer, int) with MappedByteBufferInputStream instead of byte array.
File resize interface allowing a file to change its size, e.g.