| /*========================================================================= |
| * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved. |
| * This product is protected by U.S. and international copyright |
| * and intellectual property laws. Pivotal products are covered by |
| * one or more patents listed at http://www.pivotal.io/patents. |
| *========================================================================= |
| */ |
| package com.gemstone.gemfire.pdx.internal;
|
|
|
| import java.io.ByteArrayOutputStream;
|
| import java.io.DataOutput;
|
| import java.io.DataOutputStream;
|
| import java.io.IOException;
|
| import java.nio.ByteBuffer;
|
| import com.gemstone.gemfire.DataSerializer;
|
| import com.gemstone.gemfire.internal.DSCODE;
|
| import com.gemstone.gemfire.internal.Sendable;
|
| import com.gemstone.gemfire.internal.tcp.ByteBufferInputStream;
|
|
|
| /**
|
| * A class that references the String offset in PdxInstance
|
| * Used as Index keys for PdxInstances and
|
| * query evaluation for PdxInstances
|
| * @author Tejas Nomulwar
|
| * @since 7.0
|
| */
|
| public class PdxString implements Comparable<PdxString>, Sendable {
|
| private final byte[] bytes;
|
| private final int offset;
|
| private final byte header;
|
| //private int hash; // optimization: cache the hashcode
|
|
|
| public PdxString(byte[] bytes, int offset) {
|
| this.bytes = bytes;
|
| this.header = bytes[offset];
|
| this.offset = calcOffset(header,offset);
|
| }
|
|
|
| public PdxString(String s) {
|
| ByteArrayOutputStream bos = new ByteArrayOutputStream(s.length());
|
| try {
|
| DataSerializer.writeString(s, new DataOutputStream(bos));
|
| } catch (IOException e) {
|
| throw new IllegalStateException(e);
|
| }
|
| this.bytes = bos.toByteArray();
|
| this.header = bytes[0];
|
| this.offset = calcOffset(header, 0);
|
| }
|
|
|
| private int calcOffset( int header, int offset) {
|
| offset++; // increment offset for the header byte
|
| // length is stored as short for small strings
|
| if (header == DSCODE.STRING_BYTES || header == DSCODE.STRING) {
|
| offset += 2; // position the offset to the start of the String
|
| // (skipping header and length bytes)
|
| }
|
| // length is stored as int for huge strings
|
| else if (header == DSCODE.HUGE_STRING_BYTES || header == DSCODE.HUGE_STRING) {
|
| offset += 4; // position the offset to the start of the String
|
| // (skipping header and length bytes)
|
| }
|
| return offset;
|
| }
|
|
|
| private int getLength() {
|
| int length = 0;
|
| int lenOffset = this.offset;
|
| if (header == DSCODE.STRING_BYTES || header == DSCODE.STRING) {
|
| lenOffset -= 2;
|
| byte a = bytes[lenOffset];
|
| byte b = bytes[lenOffset + 1];
|
| length = ((a & 0xff) << 8) | (b & 0xff);
|
| }
|
| // length is stored as int for huge strings
|
| else if (header == DSCODE.HUGE_STRING_BYTES || header == DSCODE.HUGE_STRING) {
|
| lenOffset -= 4;
|
| byte a = bytes[lenOffset];
|
| byte b = bytes[lenOffset + 1];
|
| byte c = bytes[lenOffset + 2];
|
| byte d = bytes[lenOffset + 3];
|
| length = (((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff));
|
| }
|
| return length;
|
| }
|
|
|
| public int compareTo(PdxString o) {
|
| // not handling strings with different headers
|
| if(this.header != o.header){
|
| int diff =this.toString().compareTo(o.toString());
|
| return diff;
|
| }
|
| int len1 = this.getLength();
|
| int len2 = o.getLength();
|
| int n = Math.min(len1, len2);
|
|
|
| int i = this.offset;
|
| int j = o.offset;
|
|
|
| if (i == j) {
|
| int k = i;
|
| int lim = n + i;
|
| while (k < lim) {
|
| byte c1 = bytes[k];
|
| byte c2 = o.bytes[k];
|
| if (c1 != c2) {
|
| return c1 - c2;
|
| }
|
| k++;
|
| }
|
| } else {
|
| while (n-- != 0) {
|
| byte c1 = bytes[i++];
|
| byte c2 = o.bytes[j++];
|
| if (c1 != c2) {
|
| return c1 - c2;
|
| }
|
| }
|
| }
|
| return len1 - len2;
|
| }
|
|
|
| public int hashCode() {
|
| int h = 0;
|
| int len = this.getLength();
|
| if (len > 0) {
|
| int off = this.offset;
|
| for (int i = 0; i < len; i++) {
|
| h = 31 * h + bytes[off++];
|
| }
|
| }
|
| return h;
|
| }
|
|
|
| public boolean equals(Object anObject) {
|
| if (this == anObject) {
|
| return true;
|
| }
|
| if (anObject instanceof PdxString) {
|
| PdxString o = (PdxString) anObject;
|
| if(this.header != o.header){ //header needs to be same for Pdxstrings to be equal
|
| return false;
|
| }
|
| int n = this.getLength();
|
| if (n == o.getLength()) {
|
| int i = this.offset;
|
| int j = o.offset;
|
| while (n-- != 0) {
|
| if (bytes[i++] != o.bytes[j++])
|
| return false;
|
| }
|
| return true;
|
| }
|
| }
|
| return false;
|
| }
|
|
|
|
|
| public String toString() {
|
| String s = null;
|
| int headerOffset = this.offset;
|
| try {
|
| --headerOffset; // for header byte
|
| if (header == DSCODE.STRING_BYTES || header == DSCODE.STRING) {
|
| headerOffset -= 2; // position the offset to the start of the String (skipping
|
| // header and length bytes)
|
| }
|
| // length is stored as int for huge strings
|
| else if (header == DSCODE.HUGE_STRING_BYTES
|
| || header == DSCODE.HUGE_STRING) {
|
| headerOffset -= 4;
|
| }
|
| ByteBuffer stringByteBuffer = ByteBuffer.wrap(bytes, headerOffset, bytes.length
|
| - headerOffset); // Wrapping more bytes than the actual String bytes in
|
| // array. Counting on the readString() to read only String
|
| // bytes
|
| s = DataSerializer.readString(new ByteBufferInputStream (stringByteBuffer));
|
| } catch (IOException e) {
|
| throw new IllegalStateException(e);
|
| }
|
| return s;
|
| }
|
|
|
| @Override
|
| public void sendTo(DataOutput out) throws IOException {
|
| int offset = this.offset;
|
| int len = getLength();
|
| --offset; // for header byte
|
| len++;
|
| if (header == DSCODE.STRING_BYTES || header == DSCODE.STRING) {
|
| len+=2;
|
| offset -= 2;
|
| }
|
| else if (header == DSCODE.HUGE_STRING_BYTES || header == DSCODE.HUGE_STRING) {
|
| len+=4;
|
| offset -= 4;
|
| }
|
| out.write(bytes, offset, len);
|
| }
|
|
|
| }
|