/*
* Licensed to GraphHopper GmbH under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper GmbH licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.graphhopper.search;
import com.graphhopper.storage.DataAccess;
import com.graphhopper.storage.Directory;
import com.graphhopper.storage.Storable;
import com.graphhopper.util.BitUtil;
import com.graphhopper.util.Helper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Ottavio Campana
* @author Peter Karich
*/
public class NameIndex implements Storable<NameIndex> {
private static final Logger logger = LoggerFactory.getLogger(NameIndex.class);
private static final long START_POINTER = 1;
private final DataAccess names;
private long bytePointer = START_POINTER;
// minor optimization for the previous stored name
private String lastName;
private long lastIndex;
public NameIndex(Directory dir) {
names = dir.find("names");
}
@Override
public NameIndex create(long initBytes) {
names.create(initBytes);
return this;
}
@Override
public boolean loadExisting() {
if (names.loadExisting()) {
bytePointer = BitUtil.LITTLE.combineIntsToLong(names.getHeader(0), names.getHeader(4));
return true;
}
return false;
}
/**
* @return the byte pointer to the name
*/
public long put(String name) {
if (name == null || name.isEmpty()) {
return 0;
}
if (name.equals(lastName)) {
return lastIndex;
}
byte[] bytes = getBytes(name);
long oldPointer = bytePointer;
names.ensureCapacity(bytePointer + 1 + bytes.length);
byte[] sizeBytes = new byte[]{
(byte) bytes.length
};
names.setBytes(bytePointer, sizeBytes, sizeBytes.length);
bytePointer++;
names.setBytes(bytePointer, bytes, bytes.length);
bytePointer += bytes.length;
lastName = name;
lastIndex = oldPointer;
return oldPointer;
}
private byte[] getBytes(String name) {
byte[] bytes = null;
for (int i = 0; i < 2; i++) {
bytes = name.getBytes(Helper.UTF_CS);
// we have to store the size of the array into *one* byte
if (bytes.length > 255) {
String newName = name.substring(0, 256 / 4);
logger.info("Way name is too long: " + name + " truncated to " + newName);
name = newName;
continue;
}
break;
}
if (bytes.length > 255) {
// really make sure no such problem exists
throw new IllegalStateException("Way name is too long: " + name);
}
return bytes;
}
public String get(long pointer) {
if (pointer < 0)
throw new IllegalStateException("Pointer to access NameIndex cannot be negative:" + pointer);
// default
if (pointer == 0)
return "";
byte[] sizeBytes = new byte[1];
names.getBytes(pointer, sizeBytes, 1);
int size = sizeBytes[0] & 0xFF;
byte[] bytes = new byte[size];
names.getBytes(pointer + sizeBytes.length, bytes, size);
return new String(bytes, Helper.UTF_CS);
}
@Override
public void flush() {
names.setHeader(0, BitUtil.LITTLE.getIntLow(bytePointer));
names.setHeader(4, BitUtil.LITTLE.getIntHigh(bytePointer));
names.flush();
}
@Override
public void close() {
names.close();
}
@Override
public boolean isClosed() {
return names.isClosed();
}
public void setSegmentSize(int segments) {
names.setSegmentSize(segments);
}
@Override
public long getCapacity() {
return names.getCapacity();
}
public void copyTo(NameIndex nameIndex) {
names.copyTo(nameIndex.names);
}
}