Rev 2045 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Copyright (C) 2006 Steve Ratcliffe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* Author: Steve Ratcliffe
* Create date: 07-Dec-2006
*/
package uk.me.parabola.imgfmt.app;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import uk.me.parabola.imgfmt.MapFailedException;
import uk.me.parabola.imgfmt.fs.ImgChannel;
import uk.me.parabola.log.Logger;
/**
* A straight forward implementation that just keeps all the data in a buffer
* until the file needs to be written to disk.
*
* @author Steve Ratcliffe
*/
public class BufferedImgFileWriter
implements ImgFileWriter
{
private static final Logger log =
Logger.
getLogger(BufferedImgFileWriter.
class);
private static final int KBYTE =
1024;
private static final int INIT_SIZE =
16 * KBYTE
;
private static final int GROW_SIZE =
128 * KBYTE
;
private static final int GUARD_SIZE = KBYTE
;
private final ImgChannel chan
;
private ByteBuffer buf =
ByteBuffer.
allocate(INIT_SIZE
);
private int bufferSize = INIT_SIZE
;
// The size of the file. Note that for this to be set properly, the
// position must be set to a low value after the full file is written. This
// always happens because we go back and write the header after all is
// written.
private int maxSize
;
// The maximum allowed file size.
private long maxAllowedSize = 0xffffff
;
public BufferedImgFileWriter
(ImgChannel chan
) {
this.
chan = chan
;
buf.
order(ByteOrder.
LITTLE_ENDIAN);
}
/**
* Called to write out any saved buffers. The strategy may write
* directly to the file in which case this would have nothing or
* little to do.
*/
public void sync
() throws IOException {
buf.
limit(maxSize
);
buf.
position(0);
log.
debug("syncing to pos", chan.
position(),
", size", buf.
limit());
chan.
write(buf
);
}
/**
* Get the position. Needed because may not be reflected in the underlying
* file if being buffered.
*
* @return The logical position within the file.
*/
public int position
() {
return buf.
position();
}
/**
* Set the position of the file.
*
* @param pos The new position in the file.
*/
public void position
(long pos
) {
int cur = position
();
if (cur
> maxSize
)
maxSize = cur
;
buf.
position((int) pos
);
}
/**
* Called when the stream is closed. Any resources can be freed.
*/
public void close
() throws IOException {
chan.
close();
}
/**
* Write out a single byte.
*
* @param b The byte to write.
*/
public void put
(byte b
) {
ensureSize
(1);
buf.
put(b
);
}
/**
* Write out two bytes. Done in the correct byte order.
*
* @param c The value to write.
*/
public void putChar
(char c
) {
ensureSize
(2);
buf.
putChar(c
);
}
/**
* Write out a 3 byte value in the correct byte order etc.
*
* @param val The value to write.
*/
public void put3
(int val
) {
ensureSize
(3);
buf.
put((byte) (val
& 0xff
));
buf.
putChar((char) (val
>> 8));
}
/**
* Write out 4 byte value.
*
* @param val The value to write.
*/
public void putInt
(int val
) {
ensureSize
(4);
buf.
putInt(val
);
}
/**
* Write out an arbitrary length sequence of bytes.
*
* @param val The values to write.
*/
public void put
(byte[] val
) {
ensureSize
(val.
length);
buf.
put(val
);
}
/**
* Write out part of a byte array.
*
* @param src The array to take bytes from.
* @param start The start position.
* @param length The number of bytes to write.
*/
public void put
(byte[] src,
int start,
int length
) {
ensureSize
(length
);
buf.
put(src, start, length
);
}
public void put
(ByteBuffer src
) {
ensureSize
(src.
limit());
buf.
put(src
);
}
/**
* Get the size of the file as written.
*
* NOTE: that calling this is only valid at certain times.
*
* @return The size of the file, if it is available.
*/
public long getSize
() {
return maxSize
;
}
public ByteBuffer getBuffer
() {
return buf
;
}
/**
* Make sure there is enough room for the data we are about to write.
*
* @param length The amount of data.
*/
private void ensureSize
(int length
) {
int needed = buf.
position() + length
;
if (needed
> (bufferSize - GUARD_SIZE
)) {
while(needed
> (bufferSize - GUARD_SIZE
))
bufferSize += GROW_SIZE
;
if (bufferSize
> maxAllowedSize
) {
// Previous message was confusing people, although it is difficult to come
// up with something that is strictly true in all situations.
throw new MapFailedException
(
"There is not enough room in a single garmin map for all the input data\n" +
" The .osm file should be split into smaller pieces first.");
}
ByteBuffer newb =
ByteBuffer.
allocate(bufferSize
);
newb.
order(ByteOrder.
LITTLE_ENDIAN);
buf.
flip();
newb.
put(buf
);
buf = newb
;
}
}
public void setMaxSize
(long maxSize
) {
this.
maxAllowedSize = maxSize
;
}
}