blob: 8f32b5c6c2d62203e3efe98332027b26b7919095 [file] [log] [blame]
package com.fasterxml.jackson.core.io;
import java.io.*;
import java.nio.ByteBuffer;
import com.fasterxml.jackson.core.SerializableString;
/**
* String token that can lazily serialize String contained and then reuse that
* serialization later on. This is similar to JDBC prepared statements, for example,
* in that instances should only be created when they are used more than use;
* prime candidates are various serializers.
*<p>
* Class is final for performance reasons and since this is not designed to
* be extensible or customizable (customizations would occur in calling code)
*/
public class SerializedString
implements SerializableString, java.io.Serializable
{
private static final long serialVersionUID = 1L;
protected final String _value;
/* 13-Dec-2010, tatu: Whether use volatile or not is actually an important
* decision for multi-core use cases. Cost of volatility can be non-trivial
* for heavy use cases, and serialized-string instances are accessed often.
* Given that all code paths with common Jackson usage patterns go through
* a few memory barriers (mostly with cache/reuse pool access) it seems safe
* enough to omit volatiles here, given how simple lazy initialization is.
* This can be compared to how {@link String#hashCode} works; lazily and
* without synchronization or use of volatile keyword.
*
* Change to remove volatile was a request by implementors of a high-throughput
* search framework; and they believed this is an important optimization for
* heaviest, multi-core deployed use cases.
*/
/*
* 22-Sep-2013, tatu: FWIW, there have been no reports of problems in this
* area, or anything pointing to it. So I think we are safe up to JDK7
* and hopefully beyond.
*/
protected /*volatile*/ byte[] _quotedUTF8Ref;
protected /*volatile*/ byte[] _unquotedUTF8Ref;
protected /*volatile*/ char[] _quotedChars;
public SerializedString(String v) {
if (v == null) {
throw new IllegalStateException("Null String illegal for SerializedString");
}
_value = v;
}
/*
/**********************************************************
/* Serializable overrides
/**********************************************************
*/
/**
* Ugly hack, to work through the requirement that _value is indeed final,
* and that JDK serialization won't call ctor(s).
*
* @since 2.1
*/
protected transient String _jdkSerializeValue;
private void readObject(ObjectInputStream in) throws IOException {
_jdkSerializeValue = in.readUTF();
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeUTF(_value);
}
protected Object readResolve() {
return new SerializedString(_jdkSerializeValue);
}
/*
/**********************************************************
/* API
/**********************************************************
*/
@Override
public final String getValue() { return _value; }
/**
* Returns length of the String as characters
*/
@Override
public final int charLength() { return _value.length(); }
@Override
public final char[] asQuotedChars() {
char[] result = _quotedChars;
if (result == null) {
result = JsonStringEncoder.getInstance().quoteAsString(_value);
_quotedChars = result;
}
return result;
}
/**
* Accessor for accessing value that has been quoted using JSON
* quoting rules, and encoded using UTF-8 encoding.
*/
@Override
public final byte[] asUnquotedUTF8() {
byte[] result = _unquotedUTF8Ref;
if (result == null) {
result = JsonStringEncoder.getInstance().encodeAsUTF8(_value);
_unquotedUTF8Ref = result;
}
return result;
}
/**
* Accessor for accessing value as is (without JSON quoting)
* encoded using UTF-8 encoding.
*/
@Override
public final byte[] asQuotedUTF8() {
byte[] result = _quotedUTF8Ref;
if (result == null) {
result = JsonStringEncoder.getInstance().quoteAsUTF8(_value);
_quotedUTF8Ref = result;
}
return result;
}
/*
/**********************************************************
/* Additional 2.0 methods for appending/writing contents
/**********************************************************
*/
@Override
public int appendQuotedUTF8(byte[] buffer, int offset) {
byte[] result = _quotedUTF8Ref;
if (result == null) {
result = JsonStringEncoder.getInstance().quoteAsUTF8(_value);
_quotedUTF8Ref = result;
}
final int length = result.length;
if ((offset + length) > buffer.length) {
return -1;
}
System.arraycopy(result, 0, buffer, offset, length);
return length;
}
@Override
public int appendQuoted(char[] buffer, int offset) {
char[] result = _quotedChars;
if (result == null) {
result = JsonStringEncoder.getInstance().quoteAsString(_value);
_quotedChars = result;
}
final int length = result.length;
if ((offset + length) > buffer.length) {
return -1;
}
System.arraycopy(result, 0, buffer, offset, length);
return length;
}
@Override
public int appendUnquotedUTF8(byte[] buffer, int offset) {
byte[] result = _unquotedUTF8Ref;
if (result == null) {
result = JsonStringEncoder.getInstance().encodeAsUTF8(_value);
_unquotedUTF8Ref = result;
}
final int length = result.length;
if ((offset + length) > buffer.length) {
return -1;
}
System.arraycopy(result, 0, buffer, offset, length);
return length;
}
@Override
public int appendUnquoted(char[] buffer, int offset) {
String str = _value;
final int length = str.length();
if ((offset + length) > buffer.length) {
return -1;
}
str.getChars(0, length, buffer, offset);
return length;
}
@Override
public int writeQuotedUTF8(OutputStream out) throws IOException {
byte[] result = _quotedUTF8Ref;
if (result == null) {
result = JsonStringEncoder.getInstance().quoteAsUTF8(_value);
_quotedUTF8Ref = result;
}
final int length = result.length;
out.write(result, 0, length);
return length;
}
@Override
public int writeUnquotedUTF8(OutputStream out) throws IOException {
byte[] result = _unquotedUTF8Ref;
if (result == null) {
result = JsonStringEncoder.getInstance().encodeAsUTF8(_value);
_unquotedUTF8Ref = result;
}
final int length = result.length;
out.write(result, 0, length);
return length;
}
@Override
public int putQuotedUTF8(ByteBuffer buffer) {
byte[] result = _quotedUTF8Ref;
if (result == null) {
result = JsonStringEncoder.getInstance().quoteAsUTF8(_value);
_quotedUTF8Ref = result;
}
final int length = result.length;
if (length > buffer.remaining()) {
return -1;
}
buffer.put(result, 0, length);
return length;
}
@Override
public int putUnquotedUTF8(ByteBuffer buffer) {
byte[] result = _unquotedUTF8Ref;
if (result == null) {
result = JsonStringEncoder.getInstance().encodeAsUTF8(_value);
_unquotedUTF8Ref = result;
}
final int length = result.length;
if (length > buffer.remaining()) {
return -1;
}
buffer.put(result, 0, length);
return length;
}
/*
/**********************************************************
/* Standard method overrides
/**********************************************************
*/
@Override
public final String toString() { return _value; }
@Override
public final int hashCode() { return _value.hashCode(); }
@Override
public final boolean equals(Object o) {
if (o == this) return true;
if (o == null || o.getClass() != getClass()) return false;
SerializedString other = (SerializedString) o;
return _value.equals(other._value);
}
}