Bug #61136 - Improve Common SS formula evaluation speed by implementing lazy evaluation
This commit is contained in:
parent
d472f88a42
commit
8dec89756a
@ -41,11 +41,12 @@ public class Formula {
|
|||||||
|
|
||||||
private static final Formula EMPTY = new Formula(new byte[0], 0);
|
private static final Formula EMPTY = new Formula(new byte[0], 0);
|
||||||
|
|
||||||
/** immutable */
|
/** immutable, lazily populated */
|
||||||
private final byte[] _byteEncoding;
|
private byte[] _byteEncoding;
|
||||||
private final int _encodedTokenLen;
|
private int _encodedTokenLen = -1;
|
||||||
|
private Ptg[] _ptgTokens;
|
||||||
|
|
||||||
private Formula(byte[] byteEncoding, int encodedTokenLen) {
|
private Formula(final byte[] byteEncoding, final int encodedTokenLen) {
|
||||||
_byteEncoding = byteEncoding.clone();
|
_byteEncoding = byteEncoding.clone();
|
||||||
_encodedTokenLen = encodedTokenLen;
|
_encodedTokenLen = encodedTokenLen;
|
||||||
|
|
||||||
@ -64,6 +65,14 @@ public class Formula {
|
|||||||
public static Formula read(int encodedTokenLen, LittleEndianInput in) {
|
public static Formula read(int encodedTokenLen, LittleEndianInput in) {
|
||||||
return read(encodedTokenLen, in, encodedTokenLen);
|
return read(encodedTokenLen, in, encodedTokenLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Formula(final Ptg[] ptgs) {
|
||||||
|
// cachedTokens should only be provided by a source that promises not to mutate the array
|
||||||
|
// Since this class itself doesn't mutate _ptgTokens, as long as this promise is honored,
|
||||||
|
// _ptgTokens is immutable.
|
||||||
|
_ptgTokens = ptgs.clone();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When there are no array constants present, <tt>encodedTokenLen</tt>==<tt>totalEncodedLen</tt>
|
* When there are no array constants present, <tt>encodedTokenLen</tt>==<tt>totalEncodedLen</tt>
|
||||||
* @param encodedTokenLen number of bytes in the stream taken by the plain formula tokens
|
* @param encodedTokenLen number of bytes in the stream taken by the plain formula tokens
|
||||||
@ -78,9 +87,49 @@ public class Formula {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Ptg[] getTokens() {
|
public Ptg[] getTokens() {
|
||||||
LittleEndianInput in = new LittleEndianByteArrayInputStream(_byteEncoding);
|
if (_ptgTokens == null) {
|
||||||
return Ptg.readTokens(_encodedTokenLen, in);
|
readPtgTokensFromBytes();
|
||||||
}
|
}
|
||||||
|
return _ptgTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getEncodedBytes() {
|
||||||
|
if (_byteEncoding == null) {
|
||||||
|
readBytesFromPtgTokens();
|
||||||
|
}
|
||||||
|
return _byteEncoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getEncodedTokenLen() {
|
||||||
|
if (_encodedTokenLen <= 0) {
|
||||||
|
readBytesFromPtgTokens();
|
||||||
|
}
|
||||||
|
return _encodedTokenLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readPtgTokensFromBytes() {
|
||||||
|
if (_byteEncoding == null || _encodedTokenLen <= 0) {
|
||||||
|
// EMPTY
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LittleEndianInput in = new LittleEndianByteArrayInputStream(_byteEncoding);
|
||||||
|
_ptgTokens = Ptg.readTokens(_encodedTokenLen, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readBytesFromPtgTokens() {
|
||||||
|
if (_ptgTokens == null) {
|
||||||
|
// EMPTY
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final int totalSize = Ptg.getEncodedSize(_ptgTokens);
|
||||||
|
final byte[] encodedData = new byte[totalSize];
|
||||||
|
Ptg.serializePtgs(_ptgTokens, encodedData, 0);
|
||||||
|
final int encodedTokenLen = Ptg.getEncodedSizeWithoutArrayData(_ptgTokens);
|
||||||
|
|
||||||
|
_byteEncoding = encodedData;
|
||||||
|
_encodedTokenLen = encodedTokenLen;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes The formula encoding is includes:
|
* Writes The formula encoding is includes:
|
||||||
* <ul>
|
* <ul>
|
||||||
@ -90,16 +139,18 @@ public class Formula {
|
|||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public void serialize(LittleEndianOutput out) {
|
public void serialize(LittleEndianOutput out) {
|
||||||
out.writeShort(_encodedTokenLen);
|
out.writeShort(getEncodedTokenLen());
|
||||||
out.write(_byteEncoding);
|
out.write(getEncodedBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void serializeTokens(LittleEndianOutput out) {
|
public void serializeTokens(LittleEndianOutput out) {
|
||||||
out.write(_byteEncoding, 0, _encodedTokenLen);
|
out.write(getEncodedBytes(), 0, getEncodedTokenLen());
|
||||||
}
|
}
|
||||||
public void serializeArrayConstantData(LittleEndianOutput out) {
|
public void serializeArrayConstantData(LittleEndianOutput out) {
|
||||||
int len = _byteEncoding.length-_encodedTokenLen;
|
byte[] bytes = getEncodedBytes();
|
||||||
out.write(_byteEncoding, _encodedTokenLen, len);
|
int encodedLen = getEncodedTokenLen();
|
||||||
|
int len = bytes.length - encodedLen;
|
||||||
|
out.write(bytes, encodedLen, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -113,7 +164,7 @@ public class Formula {
|
|||||||
* Note - this value is different to <tt>tokenDataLength</tt>
|
* Note - this value is different to <tt>tokenDataLength</tt>
|
||||||
*/
|
*/
|
||||||
public int getEncodedSize() {
|
public int getEncodedSize() {
|
||||||
return 2 + _byteEncoding.length;
|
return 2 + getEncodedBytes().length;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This method is often used when the formula length does not appear immediately before
|
* This method is often used when the formula length does not appear immediately before
|
||||||
@ -123,7 +174,8 @@ public class Formula {
|
|||||||
* the leading ushort field, nor any trailing array constant data.
|
* the leading ushort field, nor any trailing array constant data.
|
||||||
*/
|
*/
|
||||||
public int getEncodedTokenSize() {
|
public int getEncodedTokenSize() {
|
||||||
return _encodedTokenLen;
|
|
||||||
|
return getEncodedTokenLen();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,15 +184,11 @@ public class Formula {
|
|||||||
* @param ptgs may be <code>null</code>
|
* @param ptgs may be <code>null</code>
|
||||||
* @return Never <code>null</code> (Possibly empty if the supplied <tt>ptgs</tt> is <code>null</code>)
|
* @return Never <code>null</code> (Possibly empty if the supplied <tt>ptgs</tt> is <code>null</code>)
|
||||||
*/
|
*/
|
||||||
public static Formula create(Ptg[] ptgs) {
|
public static Formula create(final Ptg[] ptgs) {
|
||||||
if (ptgs == null || ptgs.length < 1) {
|
if (ptgs == null || ptgs.length < 1) {
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}
|
}
|
||||||
int totalSize = Ptg.getEncodedSize(ptgs);
|
return new Formula(ptgs);
|
||||||
byte[] encodedData = new byte[totalSize];
|
|
||||||
Ptg.serializePtgs(ptgs, encodedData, 0);
|
|
||||||
int encodedTokenLen = Ptg.getEncodedSizeWithoutArrayData(ptgs);
|
|
||||||
return new Formula(encodedData, encodedTokenLen);
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Gets the {@link Ptg} array from the supplied {@link Formula}.
|
* Gets the {@link Ptg} array from the supplied {@link Formula}.
|
||||||
@ -172,7 +220,7 @@ public class Formula {
|
|||||||
* belongs to. <code>null</code> if this formula is not part of an array or shared formula.
|
* belongs to. <code>null</code> if this formula is not part of an array or shared formula.
|
||||||
*/
|
*/
|
||||||
public CellReference getExpReference() {
|
public CellReference getExpReference() {
|
||||||
byte[] data = _byteEncoding;
|
final byte[] data = getEncodedBytes();
|
||||||
if (data.length != 5) {
|
if (data.length != 5) {
|
||||||
// tExp and tTbl are always 5 bytes long, and the only ptg in the formula
|
// tExp and tTbl are always 5 bytes long, and the only ptg in the formula
|
||||||
return null;
|
return null;
|
||||||
@ -185,11 +233,11 @@ public class Formula {
|
|||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int firstRow = LittleEndian.getUShort(data, 1);
|
final int firstRow = LittleEndian.getUShort(data, 1);
|
||||||
int firstColumn = LittleEndian.getUShort(data, 3);
|
final int firstColumn = LittleEndian.getUShort(data, 3);
|
||||||
return new CellReference(firstRow, firstColumn);
|
return new CellReference(firstRow, firstColumn);
|
||||||
}
|
}
|
||||||
public boolean isSame(Formula other) {
|
public boolean isSame(Formula other) {
|
||||||
return Arrays.equals(_byteEncoding, other._byteEncoding);
|
return Arrays.equals(getEncodedBytes(), other.getEncodedBytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user