extract LwHttp from MapDatabase
This commit is contained in:
parent
99dafda4b9
commit
d25d967332
431
src/org/oscim/database/oscimap/LwHttp.java
Normal file
431
src/org/oscim/database/oscimap/LwHttp.java
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
* Copyright 2013 OpenScienceMap
|
||||
*
|
||||
* 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.oscimap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URL;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
public class LwHttp {
|
||||
private static final String TAG = LwHttp.class.getName();
|
||||
|
||||
private String mHost;
|
||||
private int mPort;
|
||||
private long mContentLenth;
|
||||
private InputStream mInputStream;
|
||||
|
||||
private final int BUFFER_SIZE = 65536;
|
||||
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
// position in buffer
|
||||
int bufferPos;
|
||||
|
||||
// bytes available in buffer
|
||||
int bufferFill;
|
||||
|
||||
// overall bytes of content read
|
||||
private int mReadPos;
|
||||
|
||||
private int mMaxReq = 0;
|
||||
private Socket mSocket;
|
||||
private OutputStream mCommandStream;
|
||||
private InputStream mResponseStream;
|
||||
long mLastRequest = 0;
|
||||
private SocketAddress mSockAddr;
|
||||
|
||||
private final static byte[] RESPONSE_HTTP_OK = "HTTP/1.1 200 OK".getBytes();
|
||||
private final static int RESPONSE_EXPECTED_LIVES = 100;
|
||||
private final static int RESPONSE_EXPECTED_TIMEOUT = 10000;
|
||||
|
||||
private byte[] REQUEST_GET_START;
|
||||
private byte[] REQUEST_GET_END;
|
||||
|
||||
private byte[] mRequestBuffer;
|
||||
|
||||
boolean setServer(String urlString) {
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(urlString);
|
||||
} catch (MalformedURLException e) {
|
||||
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
//return new OpenResult("invalid url: " + options.get("url"));
|
||||
}
|
||||
|
||||
int port = url.getPort();
|
||||
if (port < 0)
|
||||
port = 80;
|
||||
|
||||
String host = url.getHost();
|
||||
String path = url.getPath();
|
||||
Log.d(TAG, "open oscim database: " + host + " " + port + " " + path);
|
||||
|
||||
REQUEST_GET_START = ("GET " + path).getBytes();
|
||||
REQUEST_GET_END = (".osmtile HTTP/1.1\n" +
|
||||
"Host: " + host + "\n" +
|
||||
"Connection: Keep-Alive\n\n").getBytes();
|
||||
|
||||
mHost = host;
|
||||
mPort = port;
|
||||
|
||||
mRequestBuffer = new byte[1024];
|
||||
System.arraycopy(REQUEST_GET_START, 0,
|
||||
mRequestBuffer, 0, REQUEST_GET_START.length);
|
||||
return true;
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (mSocket != null) {
|
||||
try {
|
||||
mSocket.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
mSocket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int readHeader() throws IOException {
|
||||
InputStream is = mResponseStream;
|
||||
|
||||
byte[] buf = buffer;
|
||||
boolean first = true;
|
||||
int read = 0;
|
||||
int pos = 0;
|
||||
int end = 0;
|
||||
int len = 0;
|
||||
|
||||
// header cannot be larger than BUFFER_SIZE for this to work
|
||||
for (; pos < read || (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0; len = 0) {
|
||||
read += len;
|
||||
while (end < read && (buf[end] != '\n'))
|
||||
end++;
|
||||
|
||||
if (buf[end] == '\n') {
|
||||
if (first) {
|
||||
// check only for OK
|
||||
first = false;
|
||||
if (!compareBytes(buf, pos, end, RESPONSE_HTTP_OK, 15))
|
||||
return -1;
|
||||
|
||||
} else if (end - pos == 1) {
|
||||
// check empty line (header end)
|
||||
end += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// String line = new String(buf, pos, end - pos - 1);
|
||||
// Log.d(TAG, ">" + line + "< " + resp_len);
|
||||
|
||||
pos += (end - pos) + 1;
|
||||
end = pos;
|
||||
}
|
||||
}
|
||||
|
||||
// check 4 bytes available..
|
||||
while ((read - end) < 4 && (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0)
|
||||
read += len;
|
||||
|
||||
if (read - len < 4)
|
||||
return -1;
|
||||
|
||||
int contentLength = decodeInt(buf, end);
|
||||
mContentLenth = contentLength;
|
||||
|
||||
// buffer fill
|
||||
bufferFill = read;
|
||||
// start of content
|
||||
bufferPos = end + 4;
|
||||
// bytes of content already read into buffer
|
||||
mReadPos = read - bufferPos;
|
||||
|
||||
mInputStream = mResponseStream;
|
||||
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
boolean sendRequest(Tile tile) throws IOException {
|
||||
|
||||
bufferFill = 0;
|
||||
bufferPos = 0;
|
||||
mReadPos = 0;
|
||||
mCacheFile = null;
|
||||
|
||||
if (mSocket != null && ((mMaxReq-- <= 0)
|
||||
|| (SystemClock.elapsedRealtime() - mLastRequest
|
||||
> RESPONSE_EXPECTED_TIMEOUT))) {
|
||||
try {
|
||||
mSocket.close();
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
|
||||
// Log.d(TAG, "not alive - recreate connection " + mMaxReq);
|
||||
mSocket = null;
|
||||
}
|
||||
|
||||
if (mSocket == null) {
|
||||
lwHttpConnect();
|
||||
// we know our server
|
||||
mMaxReq = RESPONSE_EXPECTED_LIVES;
|
||||
// Log.d(TAG, "create connection");
|
||||
} else {
|
||||
// should not be needed
|
||||
int avail = mResponseStream.available();
|
||||
if (avail > 0) {
|
||||
Log.d(TAG, "Consume left-over bytes: " + avail);
|
||||
mResponseStream.read(buffer, 0, avail);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] request = mRequestBuffer;
|
||||
int pos = REQUEST_GET_START.length;
|
||||
|
||||
pos = writeInt(tile.zoomLevel, pos, request);
|
||||
request[pos++] = '/';
|
||||
pos = writeInt(tile.tileX, pos, request);
|
||||
request[pos++] = '/';
|
||||
pos = writeInt(tile.tileY, pos, request);
|
||||
|
||||
int len = REQUEST_GET_END.length;
|
||||
System.arraycopy(REQUEST_GET_END, 0, request, pos, len);
|
||||
len += pos;
|
||||
|
||||
// this does the same but with a few more allocations:
|
||||
// byte[] request = String.format(REQUEST,
|
||||
// Integer.valueOf(tile.zoomLevel),
|
||||
// Integer.valueOf(tile.tileX), Integer.valueOf(tile.tileY)).getBytes();
|
||||
|
||||
try {
|
||||
mCommandStream.write(request, 0, len);
|
||||
mCommandStream.flush();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "recreate connection");
|
||||
}
|
||||
|
||||
lwHttpConnect();
|
||||
|
||||
mCommandStream.write(request, 0, len);
|
||||
mCommandStream.flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean lwHttpConnect() throws IOException {
|
||||
if (mSockAddr == null)
|
||||
mSockAddr = new InetSocketAddress(mHost, mPort);
|
||||
|
||||
mSocket = new Socket();
|
||||
mSocket.connect(mSockAddr, 30000);
|
||||
mSocket.setTcpNoDelay(true);
|
||||
|
||||
mCommandStream = mSocket.getOutputStream(); //new BufferedOutputStream();
|
||||
mResponseStream = mSocket.getInputStream();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// write (positive) integer as char sequence to buffer
|
||||
private static int writeInt(int val, int pos, byte[] buf) {
|
||||
if (val == 0) {
|
||||
buf[pos] = '0';
|
||||
return pos + 1;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (int n = val; n > 0; n = n / 10, i++)
|
||||
buf[pos + i] = (byte) ('0' + n % 10);
|
||||
|
||||
// reverse bytes
|
||||
for (int j = pos, end = pos + i - 1, mid = pos + i / 2; j < mid; j++, end--) {
|
||||
byte tmp = buf[j];
|
||||
buf[j] = buf[end];
|
||||
buf[end] = tmp;
|
||||
}
|
||||
|
||||
return pos + i;
|
||||
}
|
||||
|
||||
private static boolean compareBytes(byte[] buffer, int position, int available,
|
||||
byte[] string, int length) {
|
||||
|
||||
if (available - position < length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
if (buffer[position + i] != string[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int decodeInt(byte[] buffer, int offset) {
|
||||
return buffer[offset] << 24 | (buffer[offset + 1] & 0xff) << 16
|
||||
| (buffer[offset + 2] & 0xff) << 8
|
||||
| (buffer[offset + 3] & 0xff);
|
||||
}
|
||||
|
||||
void readBuffer(int size) throws IOException {
|
||||
|
||||
// check if buffer already contains the request bytes
|
||||
if (bufferPos + size < bufferFill)
|
||||
return;
|
||||
|
||||
// check if inputstream is read to the end
|
||||
if (mReadPos == mContentLenth)
|
||||
return;
|
||||
int maxSize = buffer.length;
|
||||
|
||||
if (size > maxSize) {
|
||||
Log.d(TAG, "increase read buffer to " + size + " bytes");
|
||||
maxSize = size;
|
||||
byte[] tmp = new byte[maxSize];
|
||||
|
||||
bufferFill -= bufferPos;
|
||||
System.arraycopy(buffer, bufferPos, tmp, 0, bufferFill);
|
||||
bufferPos = 0;
|
||||
buffer = tmp;
|
||||
}
|
||||
|
||||
if (bufferFill == bufferPos) {
|
||||
bufferPos = 0;
|
||||
bufferFill = 0;
|
||||
} else if (bufferPos + size > maxSize) {
|
||||
// copy bytes left to the beginning of buffer
|
||||
bufferFill -= bufferPos;
|
||||
System.arraycopy(buffer, bufferPos, buffer, 0, bufferFill);
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
||||
int max = maxSize - bufferFill;
|
||||
|
||||
while ((bufferFill - bufferPos) < size && max > 0) {
|
||||
|
||||
max = maxSize - bufferFill;
|
||||
if (max > mContentLenth - mReadPos)
|
||||
max = (int) (mContentLenth - mReadPos);
|
||||
|
||||
// read until requested size is available in buffer
|
||||
int len = mInputStream.read(buffer, bufferFill, max);
|
||||
|
||||
if (len < 0) {
|
||||
// finished reading, mark end
|
||||
buffer[bufferFill] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
mReadPos += len;
|
||||
|
||||
// if (mCacheFile != null)
|
||||
// mCacheFile.write(mReadBuffer, mBufferFill, len);
|
||||
|
||||
if (mReadPos == mContentLenth)
|
||||
break;
|
||||
|
||||
bufferFill += len;
|
||||
}
|
||||
}
|
||||
|
||||
private FileOutputStream mCacheFile;
|
||||
|
||||
boolean cacheRead(Tile tile, File f) {
|
||||
if (f.exists() && f.length() > 0) {
|
||||
FileInputStream in;
|
||||
|
||||
try {
|
||||
in = new FileInputStream(f);
|
||||
|
||||
mContentLenth = f.length();
|
||||
Log.d(TAG, tile + " - using cache: " + mContentLenth);
|
||||
mInputStream = in;
|
||||
|
||||
//decode();
|
||||
in.close();
|
||||
|
||||
return true;
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
f.delete();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean cacheBegin(Tile tile, File f) {
|
||||
if (MapDatabase.USE_CACHE) {
|
||||
try {
|
||||
Log.d(TAG, tile + " - writing cache");
|
||||
mCacheFile = new FileOutputStream(f);
|
||||
|
||||
if (mReadPos > 0) {
|
||||
try {
|
||||
mCacheFile.write(buffer, bufferPos,
|
||||
bufferFill - bufferPos);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
mCacheFile = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
mCacheFile = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void cacheFinish(Tile tile, File file, boolean success) {
|
||||
if (MapDatabase.USE_CACHE) {
|
||||
if (success) {
|
||||
try {
|
||||
mCacheFile.flush();
|
||||
mCacheFile.close();
|
||||
Log.d(TAG, tile + " - cache written " + file.length());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
mCacheFile = null;
|
||||
}
|
||||
}
|
@ -15,19 +15,9 @@
|
||||
package org.oscim.database.oscimap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
|
||||
@ -52,48 +42,42 @@ import android.util.Log;
|
||||
*
|
||||
*/
|
||||
public class MapDatabase implements IMapDatabase {
|
||||
private static final String TAG = "MapDatabase";
|
||||
private static final String TAG = MapDatabase.class.getName();
|
||||
|
||||
static final boolean USE_CACHE = false;
|
||||
|
||||
private static final MapInfo mMapInfo =
|
||||
new MapInfo(new BoundingBox(-180, -90, 180, 90),
|
||||
new Byte((byte) 4), new GeoPoint(53.11, 8.85),
|
||||
null, 0, 0, 0, "de", "comment", "author", null);
|
||||
|
||||
private boolean mOpenFile = false;
|
||||
|
||||
private static final boolean USE_CACHE = false;
|
||||
private static final String CACHE_DIRECTORY = "/Android/data/org.oscim.app/cache/";
|
||||
private static final String CACHE_FILE = "%d-%d-%d.tile";
|
||||
|
||||
//private static String SERVER_ADDR = "city.informatik.uni-bremen.de";
|
||||
//private static int PORT = 80;
|
||||
//private static final String URL = "/osci/map-live/";
|
||||
//private static String TILE_URL = "/osci/oscim/";
|
||||
|
||||
private final static float REF_TILE_SIZE = 4096.0f;
|
||||
|
||||
// 'open' state
|
||||
private boolean mOpen = false;
|
||||
private static File cacheDir;
|
||||
|
||||
private int MAX_TILE_TAGS = 100;
|
||||
private final int MAX_TILE_TAGS = 100;
|
||||
private Tag[] curTags = new Tag[MAX_TILE_TAGS];
|
||||
private int mCurTagCnt;
|
||||
|
||||
private IMapDatabaseCallback mMapGenerator;
|
||||
private float mScaleFactor;
|
||||
private JobTile mTile;
|
||||
private FileOutputStream mCacheFile;
|
||||
|
||||
private String mHost;
|
||||
private int mPort;
|
||||
private long mContentLenth;
|
||||
private InputStream mInputStream;
|
||||
|
||||
private final boolean debug = false;
|
||||
private LwHttp lwHttp;
|
||||
|
||||
@Override
|
||||
public QueryResult executeQuery(JobTile tile, IMapDatabaseCallback mapDatabaseCallback) {
|
||||
QueryResult result = QueryResult.SUCCESS;
|
||||
mCacheFile = null;
|
||||
|
||||
mTile = tile;
|
||||
|
||||
@ -104,24 +88,20 @@ public class MapDatabase implements IMapDatabase {
|
||||
|
||||
File f = null;
|
||||
|
||||
mBufferFill = 0;
|
||||
mBufferPos = 0;
|
||||
mReadPos = 0;
|
||||
|
||||
if (USE_CACHE) {
|
||||
f = new File(cacheDir, String.format(CACHE_FILE,
|
||||
f = new File(cacheDir, String.format(CACHE_FILE,
|
||||
Integer.valueOf(tile.zoomLevel),
|
||||
Integer.valueOf(tile.tileX),
|
||||
Integer.valueOf(tile.tileY)));
|
||||
|
||||
if (cacheRead(tile, f))
|
||||
if (lwHttp.cacheRead(tile, f))
|
||||
return QueryResult.SUCCESS;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (lwHttpSendRequest(tile) && lwHttpReadHeader() >= 0) {
|
||||
cacheBegin(tile, f);
|
||||
if (lwHttp.sendRequest(tile) && (mContentLenth = lwHttp.readHeader()) >= 0) {
|
||||
lwHttp.cacheBegin(tile, f);
|
||||
decode();
|
||||
} else {
|
||||
Log.d(TAG, tile + " Network Error");
|
||||
@ -141,22 +121,14 @@ public class MapDatabase implements IMapDatabase {
|
||||
result = QueryResult.FAILED;
|
||||
}
|
||||
|
||||
mLastRequest = SystemClock.elapsedRealtime();
|
||||
lwHttp.mLastRequest = SystemClock.elapsedRealtime();
|
||||
|
||||
if (result == QueryResult.SUCCESS) {
|
||||
|
||||
cacheFinish(tile, f, true);
|
||||
lwHttp.cacheFinish(tile, f, true);
|
||||
} else {
|
||||
cacheFinish(tile, f, false);
|
||||
if (mSocket != null) {
|
||||
// clear connection, TODO cleanup properly
|
||||
try {
|
||||
mSocket.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mSocket = null;
|
||||
}
|
||||
lwHttp.cacheFinish(tile, f, false);
|
||||
lwHttp.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -173,47 +145,23 @@ public class MapDatabase implements IMapDatabase {
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return mOpenFile;
|
||||
return mOpen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenResult open(MapOptions options) {
|
||||
if (mOpenFile)
|
||||
if (mOpen)
|
||||
return OpenResult.SUCCESS;
|
||||
|
||||
if (options == null || !options.containsKey("url"))
|
||||
return new OpenResult("options missing");
|
||||
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(options.get("url"));
|
||||
} catch (MalformedURLException e) {
|
||||
lwHttp = new LwHttp();
|
||||
|
||||
e.printStackTrace();
|
||||
if (!lwHttp.setServer(options.get("url"))){
|
||||
return new OpenResult("invalid url: " + options.get("url"));
|
||||
}
|
||||
|
||||
int port = url.getPort();
|
||||
if (port < 0)
|
||||
port = 80;
|
||||
|
||||
String host = url.getHost();
|
||||
String path = url.getPath();
|
||||
Log.d(TAG, "open oscim database: " + host + " " + port + " " + path);
|
||||
|
||||
REQUEST_GET_START = ("GET " + path).getBytes();
|
||||
REQUEST_GET_END = (".osmtile HTTP/1.1\n" +
|
||||
"Host: " + host + "\n" +
|
||||
"Connection: Keep-Alive\n\n").getBytes();
|
||||
|
||||
mHost = host;
|
||||
mPort = port;
|
||||
//mSockAddr = new InetSocketAddress(host, port);
|
||||
|
||||
mRequestBuffer = new byte[1024];
|
||||
System.arraycopy(REQUEST_GET_START, 0,
|
||||
mRequestBuffer, 0, REQUEST_GET_START.length);
|
||||
|
||||
if (USE_CACHE) {
|
||||
if (cacheDir == null) {
|
||||
String externalStorageDirectory = Environment
|
||||
@ -224,7 +172,7 @@ public class MapDatabase implements IMapDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
mOpenFile = true;
|
||||
mOpen = true;
|
||||
|
||||
initDecorder();
|
||||
|
||||
@ -233,17 +181,9 @@ public class MapDatabase implements IMapDatabase {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mOpenFile = false;
|
||||
mOpen = false;
|
||||
|
||||
if (mSocket != null) {
|
||||
try {
|
||||
mSocket.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
mSocket = null;
|
||||
}
|
||||
}
|
||||
lwHttp.close();
|
||||
|
||||
if (USE_CACHE) {
|
||||
cacheDir = null;
|
||||
@ -270,21 +210,20 @@ public class MapDatabase implements IMapDatabase {
|
||||
|
||||
// /////////////// hand sewed tile protocol buffers decoder ///////////////
|
||||
private final int MAX_WAY_COORDS = 16384;
|
||||
private final int BUFFER_SIZE = 65536;
|
||||
|
||||
private byte[] mReadBuffer = new byte[BUFFER_SIZE];
|
||||
//private final byte[] mReadBuffer = new byte[BUFFER_SIZE];
|
||||
|
||||
// position in buffer
|
||||
private int mBufferPos;
|
||||
// private int mBufferPos;
|
||||
|
||||
// bytes available in buffer
|
||||
private int mBufferFill;
|
||||
// private int mBufferFill;
|
||||
|
||||
// overall bytes of content processed
|
||||
private int mBytesProcessed;
|
||||
|
||||
// overall bytes of content read
|
||||
private int mReadPos;
|
||||
// private int mReadPos;
|
||||
|
||||
private static final int TAG_TILE_NUM_TAGS = 1;
|
||||
private static final int TAG_TILE_TAG_KEYS = 2;
|
||||
@ -305,7 +244,7 @@ public class MapDatabase implements IMapDatabase {
|
||||
|
||||
private short[] mTmpKeys = new short[100];
|
||||
private short[] mIndices = new short[10];
|
||||
private Tag[] mTmpTags = new Tag[20];
|
||||
private final Tag[] mTmpTags = new Tag[20];
|
||||
|
||||
private float[] mTmpCoords;
|
||||
|
||||
@ -533,17 +472,17 @@ public class MapDatabase implements IMapDatabase {
|
||||
private int decodeWayCoordinates(boolean skip, int nodes) throws IOException {
|
||||
int bytes = decodeVarint32();
|
||||
|
||||
readBuffer(bytes);
|
||||
lwHttp.readBuffer(bytes);
|
||||
|
||||
if (skip) {
|
||||
mBufferPos += bytes;
|
||||
lwHttp.bufferPos += bytes;
|
||||
return nodes;
|
||||
}
|
||||
|
||||
int pos = mBufferPos;
|
||||
int pos = lwHttp.bufferPos;
|
||||
int end = pos + bytes;
|
||||
float[] coords = mTmpCoords;
|
||||
byte[] buf = mReadBuffer;
|
||||
byte[] buf = lwHttp.buffer;
|
||||
int cnt = 0;
|
||||
int result;
|
||||
|
||||
@ -606,73 +545,12 @@ public class MapDatabase implements IMapDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
mBufferPos = pos;
|
||||
lwHttp.bufferPos = pos;
|
||||
mBytesProcessed += bytes;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
private void readBuffer(int size) throws IOException {
|
||||
|
||||
// check if buffer already contains the request bytes
|
||||
if (mBufferPos + size < mBufferFill)
|
||||
return;
|
||||
|
||||
// check if inputstream is read to the end
|
||||
if (mReadPos == mContentLenth)
|
||||
return;
|
||||
int maxSize = mReadBuffer.length;
|
||||
|
||||
if (size > maxSize) {
|
||||
Log.d(TAG, mTile + "increase read buffer to " + size + " bytes");
|
||||
maxSize = size;
|
||||
byte[] tmp = new byte[maxSize];
|
||||
|
||||
mBufferFill -= mBufferPos;
|
||||
System.arraycopy(mReadBuffer, mBufferPos, tmp, 0, mBufferFill);
|
||||
mBufferPos = 0;
|
||||
mReadBuffer = tmp;
|
||||
}
|
||||
|
||||
if (mBufferFill == mBufferPos) {
|
||||
mBufferPos = 0;
|
||||
mBufferFill = 0;
|
||||
} else if (mBufferPos + size > maxSize) {
|
||||
// copy bytes left to the beginning of buffer
|
||||
mBufferFill -= mBufferPos;
|
||||
System.arraycopy(mReadBuffer, mBufferPos, mReadBuffer, 0, mBufferFill);
|
||||
mBufferPos = 0;
|
||||
}
|
||||
|
||||
int max = maxSize - mBufferFill;
|
||||
|
||||
while ((mBufferFill - mBufferPos) < size && max > 0) {
|
||||
|
||||
max = maxSize - mBufferFill;
|
||||
if (max > mContentLenth - mReadPos)
|
||||
max = (int) (mContentLenth - mReadPos);
|
||||
|
||||
// read until requested size is available in buffer
|
||||
int len = mInputStream.read(mReadBuffer, mBufferFill, max);
|
||||
|
||||
if (len < 0) {
|
||||
// finished reading, mark end
|
||||
mReadBuffer[mBufferFill] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
mReadPos += len;
|
||||
|
||||
if (mCacheFile != null)
|
||||
mCacheFile.write(mReadBuffer, mBufferFill, len);
|
||||
|
||||
if (mReadPos == mContentLenth)
|
||||
break;
|
||||
|
||||
mBufferFill += len;
|
||||
}
|
||||
}
|
||||
|
||||
private short[] decodeShortArray(int num, short[] array) throws IOException {
|
||||
int bytes = decodeVarint32();
|
||||
|
||||
@ -681,13 +559,13 @@ public class MapDatabase implements IMapDatabase {
|
||||
index = new short[num];
|
||||
}
|
||||
|
||||
readBuffer(bytes);
|
||||
lwHttp.readBuffer(bytes);
|
||||
|
||||
int cnt = 0;
|
||||
|
||||
int pos = mBufferPos;
|
||||
int pos = lwHttp.bufferPos;
|
||||
int end = pos + bytes;
|
||||
byte[] buf = mReadBuffer;
|
||||
byte[] buf = lwHttp.buffer;
|
||||
int result;
|
||||
|
||||
while (pos < end) {
|
||||
@ -730,40 +608,40 @@ public class MapDatabase implements IMapDatabase {
|
||||
index[cnt++] = (short) result;
|
||||
}
|
||||
|
||||
mBufferPos = pos;
|
||||
lwHttp.bufferPos = pos;
|
||||
mBytesProcessed += bytes;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private int decodeVarint32() throws IOException {
|
||||
int pos = mBufferPos;
|
||||
int pos = lwHttp.bufferPos;
|
||||
|
||||
if (pos + 10 > mBufferFill) {
|
||||
readBuffer(4096);
|
||||
pos = mBufferPos;
|
||||
if (pos + 10 > lwHttp.bufferFill) {
|
||||
lwHttp.readBuffer(4096);
|
||||
pos = lwHttp.bufferPos;
|
||||
}
|
||||
|
||||
byte[] buf = mReadBuffer;
|
||||
byte[] buf =lwHttp.buffer;
|
||||
|
||||
if (buf[pos] >= 0) {
|
||||
mBufferPos += 1;
|
||||
lwHttp.bufferPos += 1;
|
||||
mBytesProcessed += 1;
|
||||
return buf[pos];
|
||||
} else if (buf[pos + 1] >= 0) {
|
||||
mBufferPos += 2;
|
||||
lwHttp.bufferPos += 2;
|
||||
mBytesProcessed += 2;
|
||||
return (buf[pos] & 0x7f)
|
||||
| (buf[pos + 1]) << 7;
|
||||
|
||||
} else if (buf[pos + 2] >= 0) {
|
||||
mBufferPos += 3;
|
||||
lwHttp.bufferPos += 3;
|
||||
mBytesProcessed += 3;
|
||||
return (buf[pos] & 0x7f)
|
||||
| (buf[pos + 1] & 0x7f) << 7
|
||||
| (buf[pos + 2]) << 14;
|
||||
} else if (buf[pos + 3] >= 0) {
|
||||
mBufferPos += 4;
|
||||
lwHttp.bufferPos += 4;
|
||||
mBytesProcessed += 4;
|
||||
return (buf[pos] & 0x7f)
|
||||
| (buf[pos + 1] & 0x7f) << 7
|
||||
@ -788,7 +666,7 @@ public class MapDatabase implements IMapDatabase {
|
||||
if (read == 10)
|
||||
throw new IOException("X malformed VarInt32 in " + mTile);
|
||||
|
||||
mBufferPos += read;
|
||||
lwHttp.bufferPos += read;
|
||||
mBytesProcessed += read;
|
||||
|
||||
return result;
|
||||
@ -796,10 +674,10 @@ public class MapDatabase implements IMapDatabase {
|
||||
|
||||
private String decodeString() throws IOException {
|
||||
final int size = decodeVarint32();
|
||||
readBuffer(size);
|
||||
final String result = new String(mReadBuffer, mBufferPos, size, "UTF-8");
|
||||
lwHttp.readBuffer(size);
|
||||
final String result = new String(lwHttp.buffer, lwHttp.bufferPos, size, "UTF-8");
|
||||
|
||||
mBufferPos += size;
|
||||
lwHttp.bufferPos += size;
|
||||
mBytesProcessed += size;
|
||||
return result;
|
||||
|
||||
@ -811,264 +689,4 @@ public class MapDatabase implements IMapDatabase {
|
||||
| (buffer[offset + 3] & 0xff);
|
||||
}
|
||||
|
||||
// ///////////////////////// Lightweight HttpClient ///////////////////////
|
||||
|
||||
private int mMaxReq = 0;
|
||||
private Socket mSocket;
|
||||
private OutputStream mCommandStream;
|
||||
private InputStream mResponseStream;
|
||||
private long mLastRequest = 0;
|
||||
private SocketAddress mSockAddr;
|
||||
|
||||
private final static byte[] RESPONSE_HTTP_OK = "HTTP/1.1 200 OK".getBytes();
|
||||
private final static int RESPONSE_EXPECTED_LIVES = 100;
|
||||
private final static int RESPONSE_EXPECTED_TIMEOUT = 10000;
|
||||
|
||||
private byte[] REQUEST_GET_START;
|
||||
private byte[] REQUEST_GET_END;
|
||||
|
||||
private byte[] mRequestBuffer;
|
||||
|
||||
int lwHttpReadHeader() throws IOException {
|
||||
InputStream is = mResponseStream;
|
||||
|
||||
byte[] buf = mReadBuffer;
|
||||
boolean first = true;
|
||||
int read = 0;
|
||||
int pos = 0;
|
||||
int end = 0;
|
||||
int len = 0;
|
||||
|
||||
// header cannot be larger than BUFFER_SIZE for this to work
|
||||
for (; pos < read || (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0; len = 0) {
|
||||
read += len;
|
||||
while (end < read && (buf[end] != '\n'))
|
||||
end++;
|
||||
|
||||
if (buf[end] == '\n') {
|
||||
if (first) {
|
||||
// check only for OK
|
||||
first = false;
|
||||
if (!compareBytes(buf, pos, end, RESPONSE_HTTP_OK, 15))
|
||||
return -1;
|
||||
|
||||
} else if (end - pos == 1) {
|
||||
// check empty line (header end)
|
||||
end += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// String line = new String(buf, pos, end - pos - 1);
|
||||
// Log.d(TAG, ">" + line + "< " + resp_len);
|
||||
|
||||
pos += (end - pos) + 1;
|
||||
end = pos;
|
||||
}
|
||||
}
|
||||
|
||||
// check 4 bytes available..
|
||||
while ((read - end) < 4 && (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0)
|
||||
read += len;
|
||||
|
||||
if (read - len < 4)
|
||||
return -1;
|
||||
|
||||
mContentLenth = decodeInt(buf, end);
|
||||
|
||||
// buffer fill
|
||||
mBufferFill = read;
|
||||
// start of content
|
||||
mBufferPos = end + 4;
|
||||
// bytes of content already read into buffer
|
||||
mReadPos = read - mBufferPos;
|
||||
|
||||
mInputStream = mResponseStream;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private boolean lwHttpSendRequest(Tile tile) throws IOException {
|
||||
|
||||
if (mSocket != null && ((mMaxReq-- <= 0)
|
||||
|| (SystemClock.elapsedRealtime() - mLastRequest
|
||||
> RESPONSE_EXPECTED_TIMEOUT))) {
|
||||
try {
|
||||
mSocket.close();
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
|
||||
// Log.d(TAG, "not alive - recreate connection " + mMaxReq);
|
||||
mSocket = null;
|
||||
}
|
||||
|
||||
if (mSocket == null) {
|
||||
lwHttpConnect();
|
||||
// we know our server
|
||||
mMaxReq = RESPONSE_EXPECTED_LIVES;
|
||||
// Log.d(TAG, "create connection");
|
||||
} else {
|
||||
// should not be needed
|
||||
int avail = mResponseStream.available();
|
||||
if (avail > 0) {
|
||||
Log.d(TAG, "Consume left-over bytes: " + avail);
|
||||
mResponseStream.read(mReadBuffer, 0, avail);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] request = mRequestBuffer;
|
||||
int pos = REQUEST_GET_START.length;
|
||||
|
||||
pos = writeInt(tile.zoomLevel, pos, request);
|
||||
request[pos++] = '/';
|
||||
pos = writeInt(tile.tileX, pos, request);
|
||||
request[pos++] = '/';
|
||||
pos = writeInt(tile.tileY, pos, request);
|
||||
|
||||
int len = REQUEST_GET_END.length;
|
||||
System.arraycopy(REQUEST_GET_END, 0, request, pos, len);
|
||||
len += pos;
|
||||
|
||||
// this does the same but with a few more allocations:
|
||||
// byte[] request = String.format(REQUEST,
|
||||
// Integer.valueOf(tile.zoomLevel),
|
||||
// Integer.valueOf(tile.tileX), Integer.valueOf(tile.tileY)).getBytes();
|
||||
|
||||
try {
|
||||
mCommandStream.write(request, 0, len);
|
||||
mCommandStream.flush();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, mTile + " recreate connection");
|
||||
}
|
||||
|
||||
lwHttpConnect();
|
||||
|
||||
mCommandStream.write(request, 0, len);
|
||||
mCommandStream.flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean lwHttpConnect() throws IOException {
|
||||
if (mSockAddr == null)
|
||||
mSockAddr = new InetSocketAddress(mHost, mPort);
|
||||
|
||||
mSocket = new Socket();
|
||||
mSocket.connect(mSockAddr, 30000);
|
||||
mSocket.setTcpNoDelay(true);
|
||||
|
||||
mCommandStream = mSocket.getOutputStream(); //new BufferedOutputStream();
|
||||
mResponseStream = mSocket.getInputStream();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// write (positive) integer as char sequence to buffer
|
||||
private static int writeInt(int val, int pos, byte[] buf) {
|
||||
if (val == 0) {
|
||||
buf[pos] = '0';
|
||||
return pos + 1;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (int n = val; n > 0; n = n / 10, i++)
|
||||
buf[pos + i] = (byte) ('0' + n % 10);
|
||||
|
||||
// reverse bytes
|
||||
for (int j = pos, end = pos + i - 1, mid = pos + i / 2; j < mid; j++, end--) {
|
||||
byte tmp = buf[j];
|
||||
buf[j] = buf[end];
|
||||
buf[end] = tmp;
|
||||
}
|
||||
|
||||
return pos + i;
|
||||
}
|
||||
|
||||
private static boolean compareBytes(byte[] buffer, int position, int available,
|
||||
byte[] string, int length) {
|
||||
|
||||
if (available - position < length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
if (buffer[position + i] != string[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ///////////////////////// Tile cache /////////////////////////////////
|
||||
|
||||
private boolean cacheRead(Tile tile, File f) {
|
||||
if (f.exists() && f.length() > 0) {
|
||||
FileInputStream in;
|
||||
|
||||
try {
|
||||
in = new FileInputStream(f);
|
||||
|
||||
mContentLenth = f.length();
|
||||
Log.d(TAG, tile + " - using cache: " + mContentLenth);
|
||||
mInputStream = in;
|
||||
|
||||
decode();
|
||||
in.close();
|
||||
|
||||
return true;
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
f.delete();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean cacheBegin(Tile tile, File f) {
|
||||
if (USE_CACHE) {
|
||||
try {
|
||||
Log.d(TAG, tile + " - writing cache");
|
||||
mCacheFile = new FileOutputStream(f);
|
||||
|
||||
if (mReadPos > 0) {
|
||||
try {
|
||||
mCacheFile.write(mReadBuffer, mBufferPos,
|
||||
mBufferFill - mBufferPos);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
mCacheFile = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
mCacheFile = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void cacheFinish(Tile tile, File file, boolean success) {
|
||||
if (USE_CACHE) {
|
||||
if (success) {
|
||||
try {
|
||||
mCacheFile.flush();
|
||||
mCacheFile.close();
|
||||
Log.d(TAG, tile + " - cache written " + file.length());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
mCacheFile = null;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user