544 lines
14 KiB
Java
544 lines
14 KiB
Java
/*
|
|
* Copyright 2010, 2011, 2012 mapsforge.org
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it under the
|
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
|
* Foundation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License along with
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.oscim.database.mapfile;
|
|
|
|
import java.io.IOException;
|
|
import java.io.RandomAccessFile;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.util.logging.Logger;
|
|
|
|
import org.oscim.core.Tag;
|
|
|
|
/**
|
|
* Reads from a {@link RandomAccessFile} into a buffer and decodes the data.
|
|
*/
|
|
public class ReadBuffer {
|
|
private static final String CHARSET_UTF8 = "UTF-8";
|
|
private static final Logger LOG = Logger.getLogger(ReadBuffer.class.getName());
|
|
|
|
/**
|
|
* Maximum buffer size which is supported by this implementation.
|
|
*/
|
|
static final int MAXIMUM_BUFFER_SIZE = 8000000;
|
|
|
|
private byte[] mBufferData;
|
|
private int mBufferPosition;
|
|
private final RandomAccessFile mInputFile;
|
|
|
|
ReadBuffer(RandomAccessFile inputFile) {
|
|
mInputFile = inputFile;
|
|
}
|
|
|
|
/**
|
|
* Returns one signed byte from the read buffer.
|
|
*
|
|
* @return the byte value.
|
|
*/
|
|
public byte readByte() {
|
|
return mBufferData[mBufferPosition++];
|
|
}
|
|
|
|
/**
|
|
* Reads the given amount of bytes from the file into the read buffer and
|
|
* resets the internal buffer position. If
|
|
* the capacity of the read buffer is too small, a larger one is created
|
|
* automatically.
|
|
*
|
|
* @param length
|
|
* the amount of bytes to read from the file.
|
|
* @return true if the whole data was read successfully, false otherwise.
|
|
* @throws IOException
|
|
* if an error occurs while reading the file.
|
|
*/
|
|
public boolean readFromFile(int length) throws IOException {
|
|
// ensure that the read buffer is large enough
|
|
if (mBufferData == null || mBufferData.length < length) {
|
|
// ensure that the read buffer is not too large
|
|
if (length > MAXIMUM_BUFFER_SIZE) {
|
|
LOG.warning("invalid read length: " + length);
|
|
return false;
|
|
}
|
|
mBufferData = new byte[length];
|
|
}
|
|
|
|
mBufferPosition = 0;
|
|
|
|
// reset the buffer position and read the data into the buffer
|
|
// bufferPosition = 0;
|
|
return mInputFile.read(mBufferData, 0, length) == length;
|
|
}
|
|
|
|
/**
|
|
* Converts four bytes from the read buffer to a signed int.
|
|
* <p>
|
|
* The byte order is big-endian.
|
|
*
|
|
* @return the int value.
|
|
*/
|
|
public int readInt() {
|
|
int pos = mBufferPosition;
|
|
byte[] data = mBufferData;
|
|
mBufferPosition += 4;
|
|
|
|
return data[pos] << 24
|
|
| (data[pos + 1] & 0xff) << 16
|
|
| (data[pos + 2] & 0xff) << 8
|
|
| (data[pos + 3] & 0xff);
|
|
}
|
|
|
|
/**
|
|
* Converts eight bytes from the read buffer to a signed long.
|
|
* <p>
|
|
* The byte order is big-endian.
|
|
*
|
|
* @return the long value.
|
|
*/
|
|
public long readLong() {
|
|
int pos = mBufferPosition;
|
|
byte[] data = mBufferData;
|
|
mBufferPosition += 8;
|
|
|
|
return (data[pos] & 0xffL) << 56
|
|
| (data[pos + 1] & 0xffL) << 48
|
|
| (data[pos + 2] & 0xffL) << 40
|
|
| (data[pos + 3] & 0xffL) << 32
|
|
| (data[pos + 4] & 0xffL) << 24
|
|
| (data[pos + 5] & 0xffL) << 16
|
|
| (data[pos + 6] & 0xffL) << 8
|
|
| (data[pos + 7] & 0xffL);
|
|
|
|
}
|
|
|
|
/**
|
|
* Converts two bytes from the read buffer to a signed int.
|
|
* <p>
|
|
* The byte order is big-endian.
|
|
*
|
|
* @return the int value.
|
|
*/
|
|
public int readShort() {
|
|
mBufferPosition += 2;
|
|
return mBufferData[mBufferPosition - 2] << 8 | (mBufferData[mBufferPosition - 1] & 0xff);
|
|
}
|
|
|
|
/**
|
|
* Converts a variable amount of bytes from the read buffer to a signed int.
|
|
* <p>
|
|
* The first bit is for continuation info, the other six (last byte) or
|
|
* seven (all other bytes) bits are for data. The second bit in the last
|
|
* byte indicates the sign of the number.
|
|
*
|
|
* @return the value.
|
|
*/
|
|
public int readSignedInt() {
|
|
int pos = mBufferPosition;
|
|
byte[] data = mBufferData;
|
|
int flag;
|
|
|
|
if ((data[pos] & 0x80) == 0) {
|
|
mBufferPosition += 1;
|
|
flag = ((data[pos] & 0x40) >> 6);
|
|
|
|
return ((data[pos] & 0x3f) ^ -flag) + flag;
|
|
}
|
|
|
|
if ((data[pos + 1] & 0x80) == 0) {
|
|
mBufferPosition += 2;
|
|
flag = ((data[pos + 1] & 0x40) >> 6);
|
|
|
|
return (((data[pos] & 0x7f)
|
|
| (data[pos + 1] & 0x3f) << 7) ^ -flag) + flag;
|
|
|
|
}
|
|
|
|
if ((data[pos + 2] & 0x80) == 0) {
|
|
mBufferPosition += 3;
|
|
flag = ((data[pos + 2] & 0x40) >> 6);
|
|
|
|
return (((data[pos] & 0x7f)
|
|
| (data[pos + 1] & 0x7f) << 7
|
|
| (data[pos + 2] & 0x3f) << 14) ^ -flag) + flag;
|
|
|
|
}
|
|
|
|
if ((data[pos + 3] & 0x80) == 0) {
|
|
mBufferPosition += 4;
|
|
flag = ((data[pos + 3] & 0x40) >> 6);
|
|
|
|
return (((data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x7f) << 7)
|
|
| ((data[pos + 2] & 0x7f) << 14)
|
|
| ((data[pos + 3] & 0x3f) << 21)) ^ -flag) + flag;
|
|
}
|
|
|
|
mBufferPosition += 5;
|
|
flag = ((data[pos + 4] & 0x40) >> 6);
|
|
|
|
return ((((data[pos] & 0x7f)
|
|
| (data[pos + 1] & 0x7f) << 7
|
|
| (data[pos + 2] & 0x7f) << 14
|
|
| (data[pos + 3] & 0x7f) << 21
|
|
| (data[pos + 4] & 0x3f) << 28)) ^ -flag) + flag;
|
|
|
|
}
|
|
|
|
/**
|
|
* Converts a variable amount of bytes from the read buffer to a signed int
|
|
* array.
|
|
* <p>
|
|
* The first bit is for continuation info, the other six (last byte) or
|
|
* seven (all other bytes) bits are for data. The second bit in the last
|
|
* byte indicates the sign of the number.
|
|
*
|
|
* @param values
|
|
* result values
|
|
* @param length
|
|
* number of values to read
|
|
*/
|
|
public void readSignedInt(int[] values, int length) {
|
|
int pos = mBufferPosition;
|
|
byte[] data = mBufferData;
|
|
int flag;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
if ((data[pos] & 0x80) == 0) {
|
|
|
|
flag = ((data[pos] & 0x40) >> 6);
|
|
|
|
values[i] = ((data[pos] & 0x3f) ^ -flag) + flag;
|
|
pos += 1;
|
|
|
|
} else if ((data[pos + 1] & 0x80) == 0) {
|
|
|
|
flag = ((data[pos + 1] & 0x40) >> 6);
|
|
|
|
values[i] = (((data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x3f) << 7)) ^ -flag) + flag;
|
|
pos += 2;
|
|
|
|
} else if ((data[pos + 2] & 0x80) == 0) {
|
|
|
|
flag = ((data[pos + 2] & 0x40) >> 6);
|
|
|
|
values[i] = (((data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x7f) << 7)
|
|
| ((data[pos + 2] & 0x3f) << 14)) ^ -flag) + flag;
|
|
pos += 3;
|
|
|
|
} else if ((data[pos + 3] & 0x80) == 0) {
|
|
|
|
flag = ((data[pos + 3] & 0x40) >> 6);
|
|
|
|
values[i] = (((data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x7f) << 7)
|
|
| ((data[pos + 2] & 0x7f) << 14)
|
|
| ((data[pos + 3] & 0x3f) << 21)) ^ -flag) + flag;
|
|
|
|
pos += 4;
|
|
} else {
|
|
flag = ((data[pos + 4] & 0x40) >> 6);
|
|
|
|
values[i] = ((((data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x7f) << 7)
|
|
| ((data[pos + 2] & 0x7f) << 14)
|
|
| ((data[pos + 3] & 0x7f) << 21)
|
|
| ((data[pos + 4] & 0x3f) << 28))) ^ -flag) + flag;
|
|
|
|
pos += 5;
|
|
}
|
|
}
|
|
|
|
mBufferPosition = pos;
|
|
}
|
|
|
|
// public void readSignedInt(int[] values, int length) {
|
|
// int pos = mBufferPosition;
|
|
// byte[] data = mBufferData;
|
|
//
|
|
// for (int i = 0; i < length; i++) {
|
|
//
|
|
// if ((data[pos] & 0x80) == 0) {
|
|
// if ((data[pos] & 0x40) != 0)
|
|
// values[i] = -(data[pos] & 0x3f);
|
|
// else
|
|
// values[i] = (data[pos] & 0x3f);
|
|
// pos += 1;
|
|
// } else if ((data[pos + 1] & 0x80) == 0) {
|
|
// if ((data[pos + 1] & 0x40) != 0)
|
|
// values[i] = -((data[pos] & 0x7f)
|
|
// | ((data[pos + 1] & 0x3f) << 7));
|
|
// else
|
|
// values[i] = (data[pos] & 0x7f)
|
|
// | ((data[pos + 1] & 0x3f) << 7);
|
|
// pos += 2;
|
|
// } else if ((data[pos + 2] & 0x80) == 0) {
|
|
// if ((data[pos + 2] & 0x40) != 0)
|
|
// values[i] = -((data[pos] & 0x7f)
|
|
// | ((data[pos + 1] & 0x7f) << 7)
|
|
// | ((data[pos + 2] & 0x3f) << 14));
|
|
// else
|
|
// values[i] = (data[pos] & 0x7f)
|
|
// | ((data[pos + 1] & 0x7f) << 7)
|
|
// | ((data[pos + 2] & 0x3f) << 14);
|
|
// pos += 3;
|
|
// } else if ((data[pos + 3] & 0x80) == 0) {
|
|
// if ((data[pos + 3] & 0x40) != 0)
|
|
// values[i] = -((data[pos] & 0x7f)
|
|
// | ((data[pos + 1] & 0x7f) << 7)
|
|
// | ((data[pos + 2] & 0x7f) << 14)
|
|
// | ((data[pos + 3] & 0x3f) << 21));
|
|
// else
|
|
// values[i] = (data[pos] & 0x7f)
|
|
// | ((data[pos + 1] & 0x7f) << 7)
|
|
// | ((data[pos + 2] & 0x7f) << 14)
|
|
// | ((data[pos + 3] & 0x3f) << 21);
|
|
// pos += 4;
|
|
// } else {
|
|
// if ((data[pos + 4] & 0x40) != 0)
|
|
// values[i] = -((data[pos] & 0x7f)
|
|
// | ((data[pos + 1] & 0x7f) << 7)
|
|
// | ((data[pos + 2] & 0x7f) << 14)
|
|
// | ((data[pos + 3] & 0x7f) << 21)
|
|
// | ((data[pos + 4] & 0x3f) << 28));
|
|
// else
|
|
// values[i] = ((data[pos] & 0x7f)
|
|
// | ((data[pos + 1] & 0x7f) << 7)
|
|
// | ((data[pos + 2] & 0x7f) << 14)
|
|
// | ((data[pos + 3] & 0x7f) << 21)
|
|
// | ((data[pos + 4] & 0x3f) << 28));
|
|
// pos += 5;
|
|
// }
|
|
// }
|
|
//
|
|
// mBufferPosition = pos;
|
|
// }
|
|
|
|
/**
|
|
* Converts a variable amount of bytes from the read buffer to an unsigned
|
|
* int.
|
|
* <p>
|
|
* The first bit is for continuation info, the other seven bits are for
|
|
* data.
|
|
*
|
|
* @return the int value.
|
|
*/
|
|
public int readUnsignedInt() {
|
|
int pos = mBufferPosition;
|
|
byte[] data = mBufferData;
|
|
|
|
if ((data[pos] & 0x80) == 0) {
|
|
mBufferPosition += 1;
|
|
return (data[pos] & 0x7f);
|
|
}
|
|
|
|
if ((data[pos + 1] & 0x80) == 0) {
|
|
mBufferPosition += 2;
|
|
return (data[pos] & 0x7f)
|
|
| (data[pos + 1] & 0x7f) << 7;
|
|
}
|
|
|
|
if ((data[pos + 2] & 0x80) == 0) {
|
|
mBufferPosition += 3;
|
|
return (data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x7f) << 7)
|
|
| ((data[pos + 2] & 0x7f) << 14);
|
|
}
|
|
|
|
if ((data[pos + 3] & 0x80) == 0) {
|
|
mBufferPosition += 4;
|
|
return (data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x7f) << 7)
|
|
| ((data[pos + 2] & 0x7f) << 14)
|
|
| ((data[pos + 3] & 0x7f) << 21);
|
|
}
|
|
|
|
mBufferPosition += 5;
|
|
return (data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x7f) << 7)
|
|
| ((data[pos + 2] & 0x7f) << 14)
|
|
| ((data[pos + 3] & 0x7f) << 21)
|
|
| ((data[pos + 4] & 0x7f) << 28);
|
|
}
|
|
|
|
/**
|
|
* Decodes a variable amount of bytes from the read buffer to a string.
|
|
*
|
|
* @return the UTF-8 decoded string (may be null).
|
|
*/
|
|
public String readUTF8EncodedString() {
|
|
return readUTF8EncodedString(readUnsignedInt());
|
|
}
|
|
|
|
/**
|
|
* @return ...
|
|
*/
|
|
public int getPositionAndSkip() {
|
|
int pos = mBufferPosition;
|
|
int length = readUnsignedInt();
|
|
skipBytes(length);
|
|
return pos;
|
|
}
|
|
|
|
/**
|
|
* Decodes the given amount of bytes from the read buffer to a string.
|
|
*
|
|
* @param stringLength
|
|
* the length of the string in bytes.
|
|
* @return the UTF-8 decoded string (may be null).
|
|
*/
|
|
public String readUTF8EncodedString(int stringLength) {
|
|
if (stringLength > 0 && mBufferPosition + stringLength <= mBufferData.length) {
|
|
mBufferPosition += stringLength;
|
|
try {
|
|
return new String(mBufferData, mBufferPosition - stringLength, stringLength,
|
|
CHARSET_UTF8);
|
|
} catch (UnsupportedEncodingException e) {
|
|
throw new IllegalStateException(e);
|
|
}
|
|
}
|
|
LOG.warning("invalid string length: " + stringLength);
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Decodes a variable amount of bytes from the read buffer to a string.
|
|
*
|
|
* @param position
|
|
* buffer offset position of string
|
|
* @return the UTF-8 decoded string (may be null).
|
|
*/
|
|
public String readUTF8EncodedStringAt(int position) {
|
|
int curPosition = mBufferPosition;
|
|
mBufferPosition = position;
|
|
String result = readUTF8EncodedString(readUnsignedInt());
|
|
mBufferPosition = curPosition;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @return the current buffer position.
|
|
*/
|
|
int getBufferPosition() {
|
|
return mBufferPosition;
|
|
}
|
|
|
|
/**
|
|
* @return the current size of the read buffer.
|
|
*/
|
|
int getBufferSize() {
|
|
return mBufferData.length;
|
|
}
|
|
|
|
/**
|
|
* Sets the buffer position to the given offset.
|
|
*
|
|
* @param bufferPosition
|
|
* the buffer position.
|
|
*/
|
|
void setBufferPosition(int bufferPosition) {
|
|
mBufferPosition = bufferPosition;
|
|
}
|
|
|
|
/**
|
|
* Skips the given number of bytes in the read buffer.
|
|
*
|
|
* @param bytes
|
|
* the number of bytes to skip.
|
|
*/
|
|
void skipBytes(int bytes) {
|
|
mBufferPosition += bytes;
|
|
}
|
|
|
|
Tag[] readTags(Tag[] wayTags, byte numberOfTags) {
|
|
Tag[] tags = new Tag[numberOfTags];
|
|
|
|
int maxTag = wayTags.length;
|
|
|
|
for (byte i = 0; i < numberOfTags; i++) {
|
|
int tagId = readUnsignedInt();
|
|
if (tagId < 0 || tagId >= maxTag) {
|
|
LOG.warning("invalid tag ID: " + tagId);
|
|
return null;
|
|
}
|
|
tags[i] = wayTags[tagId];
|
|
}
|
|
return tags;
|
|
}
|
|
|
|
private static final int WAY_NUMBER_OF_TAGS_BITMASK = 0x0f;
|
|
int lastTagPosition;
|
|
|
|
int skipWays(int queryTileBitmask, int elements) {
|
|
int pos = mBufferPosition;
|
|
byte[] data = mBufferData;
|
|
int cnt = elements;
|
|
int skip;
|
|
|
|
lastTagPosition = -1;
|
|
|
|
while (cnt > 0) {
|
|
// read way size (unsigned int)
|
|
if ((data[pos] & 0x80) == 0) {
|
|
skip = (data[pos] & 0x7f);
|
|
pos += 1;
|
|
} else if ((data[pos + 1] & 0x80) == 0) {
|
|
skip = (data[pos] & 0x7f)
|
|
| (data[pos + 1] & 0x7f) << 7;
|
|
pos += 2;
|
|
} else if ((data[pos + 2] & 0x80) == 0) {
|
|
skip = (data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x7f) << 7)
|
|
| ((data[pos + 2] & 0x7f) << 14);
|
|
pos += 3;
|
|
} else if ((data[pos + 3] & 0x80) == 0) {
|
|
skip = (data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x7f) << 7)
|
|
| ((data[pos + 2] & 0x7f) << 14)
|
|
| ((data[pos + 3] & 0x7f) << 21);
|
|
pos += 4;
|
|
} else {
|
|
skip = (data[pos] & 0x7f)
|
|
| ((data[pos + 1] & 0x7f) << 7)
|
|
| ((data[pos + 2] & 0x7f) << 14)
|
|
| ((data[pos + 3] & 0x7f) << 21)
|
|
| ((data[pos + 4] & 0x7f) << 28);
|
|
pos += 5;
|
|
}
|
|
// invalid way size
|
|
if (skip < 0) {
|
|
mBufferPosition = pos;
|
|
return -1;
|
|
}
|
|
|
|
// check if way matches queryTileBitmask
|
|
if ((((data[pos] << 8) | (data[pos + 1] & 0xff)) & queryTileBitmask) == 0) {
|
|
|
|
// remember last tags position
|
|
if ((data[pos + 2] & WAY_NUMBER_OF_TAGS_BITMASK) != 0)
|
|
lastTagPosition = pos + 2;
|
|
|
|
pos += skip;
|
|
cnt--;
|
|
} else {
|
|
pos += 2;
|
|
break;
|
|
}
|
|
}
|
|
mBufferPosition = pos;
|
|
return cnt;
|
|
}
|
|
}
|