jaulib v1.3.6
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
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
51 MappedByteBufferOutputStream(final MappedByteBufferInputStream parent,
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 void setFileResizeOp(final FileResizeOp fileResizeOp)
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.