package org.exist.storage.btree; /* * dbXML License, Version 1.0 * * * Copyright (c) 1999-2001 The dbXML Group, L.L.C. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by * The dbXML Group (http://www.dbxml.com/)." * Alternately, this acknowledgment may appear in the software * itself, if and wherever such third-party acknowledgments normally * appear. * * 4. The names "dbXML" and "The dbXML Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * info@dbxml.com. * * 5. Products derived from this software may not be called "dbXML", * nor may "dbXML" appear in their name, without prior written * permission of The dbXML Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE DBXML GROUP OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id$ */ import java.io.UnsupportedEncodingException; import org.apache.log4j.Logger; /** * Value is the primary base class for all data storing objects. * The content window of Value objects are immutable, but the * underlying byte array is not. */ public class Value implements Comparable { public final static Value EMPTY_VALUE = new Value(new byte[0]); private static Logger LOG = Logger.getLogger(Value.class.getName()); protected long address = -1; protected byte[] data = null; protected int pos = 0; protected int len = -1; public Value() { } public Value(Value value) { data = value.data; pos = value.pos; len = value.len; } public Value(byte[] data) { this.data = data; len = data.length; } public Value(byte[] data, int pos, int len) { this.data = data; this.pos = pos; this.len = len; } public Value(String data) { try { this.data = data.getBytes("UTF-8"); } catch (UnsupportedEncodingException uee) { LOG.warn(uee); this.data = data.getBytes(); } this.len = this.data.length; } public void setAddress( long addr ) { address = addr; } public long getAddress() { return address; } /** * getData retrieves the data being stored by the Value as a byte array. * * @return The Data */ public byte[] getData() { if (pos > 0 || len < data.length) { final byte[] b = new byte[len]; System.arraycopy(data, pos, b, 0, len); return b; } else return data; } public final byte[] data() { return data; } public final int start() { return pos; } /** * getLength retrieves the length of the data being stored by the Value. * * @return The Value length */ public final int getLength() { return len; } public String toString() { return dump(); } public int hashCode() { return toString().hashCode(); } public boolean equals(Value value) { return len == value.len ? compareTo(value) == 0 : false; } public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof Value) return equals((Value) obj); else return equals(new Value(obj.toString())); } public final int compareTo(Value value) { final int dlen = value.len; final int stop = len > dlen ? dlen : len; for (int i = 0; i < stop; i++) { final byte b1 = data[pos + i]; final byte b2 = value.data[value.pos + i]; if (b1 == b2) continue; else { final short s1 = (short) (b1 & 0xFF); final short s2 = (short) (b2 & 0xFF); return s1 > s2 ? (i + 1) : - (i + 1); } } if (len == dlen) return 0; else return len > dlen ? stop + 1 : - (stop + 1); } public final int compareTo(Object obj) { if (obj instanceof Value) return compareTo((Value) obj); else return compareTo(new Value(obj.toString())); } public final int comparePrefix(Value value) { int size = value.len; for (int i = 0; i < size; i++) { final byte b1 = data[pos + i]; final byte b2 = value.data[value.pos + i]; if (b1 == b2) continue; else { final short s1 = (short) (b1 & 0xFF); final short s2 = (short) (b2 & 0xFF); return s1 > s2 ? (i + 1) : - (i + 1); } } return 0; } public final int comparePrefix(Value prefix, Value keyPrefix) { if (keyPrefix.getLength() >= prefix.getLength()) { int cmp = keyPrefix.comparePrefix(prefix); if (cmp != 0 || keyPrefix.getLength() == prefix.getLength()) return cmp; for (int i = prefix.getLength(); i < keyPrefix.getLength(); i++) { final byte b1 = data[pos + i]; final byte b2 = keyPrefix.data[keyPrefix.pos + i]; if (b1 == b2) continue; else { final short s1 = (short) (b1 & 0xFF); final short s2 = (short) (b2 & 0xFF); return s1 > s2 ? (i + 1) : - (i + 1); } } return 0; } else { return prefix.comparePrefix(keyPrefix); } } public final boolean startsWith(Value value) { if (len < value.len) return false; byte[] vdata = value.data; int vpos = value.pos; for (int i = 0; i < value.len; i++) if (data[i + pos] != vdata[i + vpos]) return false; return true; } public final boolean endsWith(Value value) { if (len < value.len) return false; byte[] vdata = value.data; int vpos = value.pos; int d = len - value.len; for (int i = 0; i < value.len; ++i) { if (data[d + i + pos] != vdata[i + vpos]) return false; } return true; } /** * Returns the length of the common prefix this value * shares with the specified other value (if any). * * @param other the other value * @return length of the common prefix, 0 if there is none */ public int commonPrefix(Value other) { final int l = Math.min(len, other.len); int i = 0; for ( ; i < l; i++) { byte b = data[pos + i]; if (b != other.data[other.pos + i]) break; } return i; } public int checkPrefix(Value prefix) { int l = Math.min(prefix.len, len); for (int i = 0; i < l; i++) { if (prefix.data[prefix.pos + i] != data[pos + i]) return i; } return l; } public Value getSeparator(Value other) { int offset = commonPrefix(other) + 1; byte[] data = new byte[Math.abs(offset)]; System.arraycopy(other.getData(), 0, data, 0, data.length); return new Value(data); } public String dump() { StringBuilder buf = new StringBuilder(); for (int i = 0; i < len; i++) { buf.append(Integer.toString(data[pos + i] & 0xFF)); buf.append(' '); } return buf.toString(); } }