toBufferList()
+ {
+ return getArrayBufferOut().toBufferList();
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageFloatOverflowException.java b/msgpack-core/src/main/java/org/msgpack/core/MessageFloatOverflowException.java
deleted file mode 100644
index 88449e6d5..000000000
--- a/msgpack-core/src/main/java/org/msgpack/core/MessageFloatOverflowException.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.msgpack.core;
-
-/**
- * This error is thrown when the user tries to read a value that has decimal component as byte, short, int and long.
- *
- */
-public class MessageFloatOverflowException extends MessageOverflowException {
-
- private final double value;
-
- public MessageFloatOverflowException(double value) {
- super();
- this.value = value;
- }
-
- public double getValue() {
- return value;
- }
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageFormat.java b/msgpack-core/src/main/java/org/msgpack/core/MessageFormat.java
index cffb525e2..d57c446f2 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessageFormat.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageFormat.java
@@ -1,15 +1,29 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core;
-import org.msgpack.core.MessagePack.Code;
import org.msgpack.core.annotations.VisibleForTesting;
import org.msgpack.value.ValueType;
-
+import org.msgpack.core.MessagePack.Code;
/**
* Describes the list of the message format types defined in the MessagePack specification.
*/
-public enum MessageFormat {
-
+public enum MessageFormat
+{
// INT7
POSFIXINT(ValueType.INTEGER),
// MAP4
@@ -24,9 +38,9 @@ public enum MessageFormat {
BIN8(ValueType.BINARY),
BIN16(ValueType.BINARY),
BIN32(ValueType.BINARY),
- EXT8(ValueType.EXTENDED),
- EXT16(ValueType.EXTENDED),
- EXT32(ValueType.EXTENDED),
+ EXT8(ValueType.EXTENSION),
+ EXT16(ValueType.EXTENSION),
+ EXT32(ValueType.EXTENSION),
FLOAT32(ValueType.FLOAT),
FLOAT64(ValueType.FLOAT),
UINT8(ValueType.INTEGER),
@@ -38,11 +52,11 @@ public enum MessageFormat {
INT16(ValueType.INTEGER),
INT32(ValueType.INTEGER),
INT64(ValueType.INTEGER),
- FIXEXT1(ValueType.EXTENDED),
- FIXEXT2(ValueType.EXTENDED),
- FIXEXT4(ValueType.EXTENDED),
- FIXEXT8(ValueType.EXTENDED),
- FIXEXT16(ValueType.EXTENDED),
+ FIXEXT1(ValueType.EXTENSION),
+ FIXEXT2(ValueType.EXTENSION),
+ FIXEXT4(ValueType.EXTENSION),
+ FIXEXT8(ValueType.EXTENSION),
+ FIXEXT16(ValueType.EXTENSION),
STR8(ValueType.STRING),
STR16(ValueType.STRING),
STR32(ValueType.STRING),
@@ -50,31 +64,34 @@ public enum MessageFormat {
ARRAY32(ValueType.ARRAY),
MAP16(ValueType.MAP),
MAP32(ValueType.MAP),
- NEGFIXINT(ValueType.INTEGER)
- ;
+ NEGFIXINT(ValueType.INTEGER);
+ private static final MessageFormat[] formatTable = new MessageFormat[256];
private final ValueType valueType;
- private MessageFormat(ValueType valueType) {
+ private MessageFormat(ValueType valueType)
+ {
this.valueType = valueType;
}
/**
* Retruns the ValueType corresponding to this MessageFormat
+ *
* @return value type
* @throws MessageFormatException if this == NEVER_USED type
*/
- public ValueType getValueType() throws MessageFormatException {
- if(this == NEVER_USED)
+ public ValueType getValueType()
+ throws MessageFormatException
+ {
+ if (this == NEVER_USED) {
throw new MessageFormatException("Cannot convert NEVER_USED to ValueType");
+ }
return valueType;
}
- private final static MessageFormat[] formatTable = new MessageFormat[256];
-
static {
// Preparing a look up table for converting byte values into MessageFormat types
- for(int b = 0; b <= 0xFF; ++b) {
+ for (int b = 0; b <= 0xFF; ++b) {
MessageFormat mf = toMessageFormat((byte) b);
formatTable[b] = mf;
}
@@ -82,20 +99,24 @@ public ValueType getValueType() throws MessageFormatException {
/**
* Returns a MessageFormat type of the specified byte value
+ *
* @param b MessageFormat of the given byte
* @return
*/
- public static MessageFormat valueOf(final byte b) {
+ public static MessageFormat valueOf(final byte b)
+ {
return formatTable[b & 0xFF];
}
/**
* Converting a byte value into MessageFormat. For faster performance, use {@link #valueOf}
+ *
* @param b MessageFormat of the given byte
* @return
*/
@VisibleForTesting
- public static MessageFormat toMessageFormat(final byte b) {
+ static MessageFormat toMessageFormat(final byte b)
+ {
if (Code.isPosFixInt(b)) {
return POSFIXINT;
}
@@ -177,5 +198,4 @@ public static MessageFormat toMessageFormat(final byte b) {
return NEVER_USED;
}
}
-
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageFormatException.java b/msgpack-core/src/main/java/org/msgpack/core/MessageFormatException.java
index f79bea279..cf169af80 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessageFormatException.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageFormatException.java
@@ -18,18 +18,21 @@
/**
* Thrown when the input message pack format is invalid
*/
-public class MessageFormatException extends MessagePackException {
-
- public MessageFormatException(Throwable e) {
+public class MessageFormatException
+ extends MessagePackException
+{
+ public MessageFormatException(Throwable e)
+ {
super(e);
}
-
- public MessageFormatException(String message) {
+ public MessageFormatException(String message)
+ {
super(message);
}
- public MessageFormatException(String message, Throwable cause) {
+ public MessageFormatException(String message, Throwable cause)
+ {
super(message, cause);
}
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageInsufficientBufferException.java b/msgpack-core/src/main/java/org/msgpack/core/MessageInsufficientBufferException.java
new file mode 100644
index 000000000..838dc77ab
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageInsufficientBufferException.java
@@ -0,0 +1,40 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core;
+
+public class MessageInsufficientBufferException
+ extends MessagePackException
+{
+ public MessageInsufficientBufferException()
+ {
+ super();
+ }
+
+ public MessageInsufficientBufferException(String message)
+ {
+ super(message);
+ }
+
+ public MessageInsufficientBufferException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ public MessageInsufficientBufferException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageIntegerOverflowException.java b/msgpack-core/src/main/java/org/msgpack/core/MessageIntegerOverflowException.java
index c3d561ec1..b3199ae2e 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessageIntegerOverflowException.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageIntegerOverflowException.java
@@ -17,36 +17,41 @@
import java.math.BigInteger;
-
/**
* This error is thrown when the user tries to read an integer value
* using a smaller types. For example, calling MessageUnpacker.unpackInt() for an integer value
* that is larger than Integer.MAX_VALUE will cause this exception.
*/
-public class MessageIntegerOverflowException extends MessageOverflowException {
-
+public class MessageIntegerOverflowException
+ extends MessageTypeException
+{
private final BigInteger bigInteger;
- public MessageIntegerOverflowException(BigInteger bigInteger) {
+ public MessageIntegerOverflowException(BigInteger bigInteger)
+ {
super();
this.bigInteger = bigInteger;
}
- public MessageIntegerOverflowException(long value) {
+ public MessageIntegerOverflowException(long value)
+ {
this(BigInteger.valueOf(value));
}
- public MessageIntegerOverflowException(String message, BigInteger bigInteger) {
+ public MessageIntegerOverflowException(String message, BigInteger bigInteger)
+ {
super(message);
this.bigInteger = bigInteger;
}
- public BigInteger getBigInteger() {
+ public BigInteger getBigInteger()
+ {
return bigInteger;
}
@Override
- public String getMessage() {
+ public String getMessage()
+ {
return bigInteger.toString();
}
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageNeverUsedFormatException.java b/msgpack-core/src/main/java/org/msgpack/core/MessageNeverUsedFormatException.java
new file mode 100644
index 000000000..726ffb497
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageNeverUsedFormatException.java
@@ -0,0 +1,38 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core;
+
+/**
+ * Thrown when the input message pack format is invalid
+ */
+public class MessageNeverUsedFormatException
+ extends MessageFormatException
+{
+ public MessageNeverUsedFormatException(Throwable e)
+ {
+ super(e);
+ }
+
+ public MessageNeverUsedFormatException(String message)
+ {
+ super(message);
+ }
+
+ public MessageNeverUsedFormatException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageOverflowException.java b/msgpack-core/src/main/java/org/msgpack/core/MessageOverflowException.java
deleted file mode 100644
index 2190a34c1..000000000
--- a/msgpack-core/src/main/java/org/msgpack/core/MessageOverflowException.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.msgpack.core;
-
-/**
- * Created on 5/28/14.
- */
-public class MessageOverflowException extends MessageTypeException {
-
- public MessageOverflowException() {
- super();
- }
-
- public MessageOverflowException(String message) {
- super(message);
- }
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessagePack.java b/msgpack-core/src/main/java/org/msgpack/core/MessagePack.java
index a6653a356..59bbbad48 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessagePack.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessagePack.java
@@ -15,7 +15,13 @@
//
package org.msgpack.core;
-import org.msgpack.core.buffer.*;
+import org.msgpack.core.buffer.ArrayBufferInput;
+import org.msgpack.core.buffer.ChannelBufferInput;
+import org.msgpack.core.buffer.ChannelBufferOutput;
+import org.msgpack.core.buffer.InputStreamBufferInput;
+import org.msgpack.core.buffer.MessageBufferInput;
+import org.msgpack.core.buffer.MessageBufferOutput;
+import org.msgpack.core.buffer.OutputStreamBufferOutput;
import java.io.InputStream;
import java.io.OutputStream;
@@ -24,190 +30,51 @@
import java.nio.charset.Charset;
import java.nio.charset.CodingErrorAction;
-import static org.msgpack.core.Preconditions.checkArgument;
-
/**
* This class has MessagePack prefix code definitions and packer/unpacker factory methods.
- *
*/
-public class MessagePack {
-
+public class MessagePack
+{
public static final Charset UTF8 = Charset.forName("UTF-8");
- /**
- * Message packer/unpacker configuration object
- */
- public static class Config {
- private final boolean readStringAsBinary;
- private final boolean readBinaryAsString;
- private final CodingErrorAction onMalFormedInput;
- private final CodingErrorAction onUnmappableCharacter;
- private final int maxUnpackStringSize;
- private final int stringEncoderBufferSize;
- private final int stringDecoderBufferSize;
- private final int packerBufferSize;
- private final int packerRawDataCopyingThreshold;
-
- public Config(
- boolean readStringAsBinary,
- boolean readBinaryAsString,
- CodingErrorAction onMalFormedInput,
- CodingErrorAction onUnmappableCharacter,
- int maxUnpackStringSize,
- int stringEncoderBufferSize,
- int stringDecoderBufferSize,
- int packerBufferSize,
- int packerRawDataCopyingThreshold) {
-
- checkArgument(packerBufferSize > 0, "packer buffer size must be larger than 0: " + packerBufferSize);
- checkArgument(stringEncoderBufferSize > 0, "string encoder buffer size must be larger than 0: " + stringEncoderBufferSize);
- checkArgument(stringDecoderBufferSize > 0, "string decoder buffer size must be larger than 0: " + stringDecoderBufferSize);
-
- this.readStringAsBinary = readStringAsBinary;
- this.readBinaryAsString = readBinaryAsString;
- this.onMalFormedInput = onMalFormedInput;
- this.onUnmappableCharacter = onUnmappableCharacter;
- this.maxUnpackStringSize = maxUnpackStringSize;
- this.stringEncoderBufferSize = stringEncoderBufferSize;
- this.stringDecoderBufferSize = stringDecoderBufferSize;
- this.packerBufferSize = packerBufferSize;
- this.packerRawDataCopyingThreshold = packerRawDataCopyingThreshold;
- }
-
- /**
- * allow unpackBinaryHeader to read str format family (default:true)
- */
- public boolean isReadStringAsBinary() { return readStringAsBinary; }
-
- /**
- * allow unpackRawStringHeader and unpackString to read bin format family (default: true)
- */
- public boolean isReadBinaryAsString() { return readBinaryAsString; }
- /**
- * Action when encountered a malformed input
- */
- public CodingErrorAction getActionOnMalFormedInput() { return onMalFormedInput; }
- /**
- * Action when an unmappable character is found
- */
- public CodingErrorAction getActionOnUnmappableCharacter() { return onUnmappableCharacter; }
-
- /**
- * unpackString size limit. (default: Integer.MAX_VALUE)
- */
- public int getMaxUnpackStringSize() { return maxUnpackStringSize; }
-
- public int getStringEncoderBufferSize() { return stringEncoderBufferSize; }
- public int getStringDecoderBufferSize() { return stringDecoderBufferSize; }
-
- public int getPackerBufferSize() { return packerBufferSize; }
- public int getPackerRawDataCopyingThreshold() { return packerRawDataCopyingThreshold; }
- }
-
- /**
- * Builder of the configuration object
- */
- public static class ConfigBuilder {
-
- private boolean readStringAsBinary = true;
- private boolean readBinaryAsString = true;
-
- private CodingErrorAction onMalFormedInput = CodingErrorAction.REPLACE;
- private CodingErrorAction onUnmappableCharacter = CodingErrorAction.REPLACE;
-
- private int maxUnpackStringSize = Integer.MAX_VALUE;
- private int stringEncoderBufferSize = 8192;
- private int stringDecoderBufferSize = 8192;
- private int packerBufferSize = 8192;
- private int packerRawDataCopyingThreshold = 512;
-
- public Config build() {
- return new Config(
- readStringAsBinary,
- readBinaryAsString,
- onMalFormedInput,
- onUnmappableCharacter,
- maxUnpackStringSize,
- stringEncoderBufferSize,
- stringDecoderBufferSize,
- packerBufferSize,
- packerRawDataCopyingThreshold
- );
- }
-
- public ConfigBuilder readStringAsBinary(boolean enable) {
- this.readStringAsBinary = enable;
- return this;
- }
- public ConfigBuilder readBinaryAsString(boolean enable) {
- this.readBinaryAsString = enable;
- return this;
- }
- public ConfigBuilder onMalFormedInput(CodingErrorAction action) {
- this.onMalFormedInput = action;
- return this;
- }
- public ConfigBuilder onUnmappableCharacter(CodingErrorAction action) {
- this.onUnmappableCharacter = action;
- return this;
- }
- public ConfigBuilder maxUnpackStringSize(int size){
- this.maxUnpackStringSize = size;
- return this;
- }
- public ConfigBuilder stringEncoderBufferSize(int size) {
- this.stringEncoderBufferSize = size;
- return this;
- }
- public ConfigBuilder stringDecoderBufferSize(int size) {
- this.stringDecoderBufferSize = size;
- return this;
- }
- public ConfigBuilder packerBufferSize(int size) {
- this.packerBufferSize = size;
- return this;
- }
- public ConfigBuilder packerRawDataCopyingThreshold(int threshold) {
- this.packerRawDataCopyingThreshold = threshold;
- return this;
- }
- }
-
-
-
- /**
- * Default configuration, which is visible only from classes in the core package.
- */
- static final Config DEFAULT_CONFIG = new ConfigBuilder().build();
-
-
/**
* The prefix code set of MessagePack. See also https://github.com/msgpack/msgpack/blob/master/spec.md for details.
*/
- public static final class Code {
-
- public static final boolean isFixInt(byte b) {
+ public static final class Code
+ {
+ public static final boolean isFixInt(byte b)
+ {
int v = b & 0xFF;
return v <= 0x7f || v >= 0xe0;
}
- public static final boolean isPosFixInt(byte b) {
+ public static final boolean isPosFixInt(byte b)
+ {
return (b & POSFIXINT_MASK) == 0;
}
- public static final boolean isNegFixInt(byte b) {
+
+ public static final boolean isNegFixInt(byte b)
+ {
return (b & NEGFIXINT_PREFIX) == NEGFIXINT_PREFIX;
}
- public static final boolean isFixStr(byte b) {
+
+ public static final boolean isFixStr(byte b)
+ {
return (b & (byte) 0xe0) == Code.FIXSTR_PREFIX;
}
- public static final boolean isFixedArray(byte b) {
+
+ public static final boolean isFixedArray(byte b)
+ {
return (b & (byte) 0xf0) == Code.FIXARRAY_PREFIX;
}
- public static final boolean isFixedMap(byte b) {
- return (b & (byte) 0xe0) == Code.FIXMAP_PREFIX;
+
+ public static final boolean isFixedMap(byte b)
+ {
+ return (b & (byte) 0xf0) == Code.FIXMAP_PREFIX;
}
- public static final boolean isFixedRaw(byte b) {
+ public static final boolean isFixedRaw(byte b)
+ {
return (b & (byte) 0xe0) == Code.FIXSTR_PREFIX;
}
@@ -258,137 +125,260 @@ public static final boolean isFixedRaw(byte b) {
public static final byte NEGFIXINT_PREFIX = (byte) 0xe0;
}
- // Packer/Unpacker factory methods
-
- private final MessagePack.Config config;
-
- public MessagePack() {
- this(MessagePack.DEFAULT_CONFIG);
- }
-
- public MessagePack(MessagePack.Config config) {
- this.config = config;
+ private MessagePack()
+ {
+ // Prohibit instantiation of this class
}
/**
- * Default MessagePack packer/unpacker factory
- */
- public static final MessagePack DEFAULT = new MessagePack(MessagePack.DEFAULT_CONFIG);
-
-
- /**
- * Create a MessagePacker that outputs the packed data to the specified stream, using the default configuration
+ * Create a packer that outputs the packed data to the specified output
+ *
* @param out
* @return
*/
- public static MessagePacker newDefaultPacker(OutputStream out) {
- return DEFAULT.newPacker(out);
+ public static MessagePacker newDefaultPacker(MessageBufferOutput out)
+ {
+ return new PackerConfig().newPacker(out);
}
/**
- * Create a MessagePacker that outputs the packed data to the specified channel, using the default configuration
- * @param channel
+ * Create a packer that outputs the packed data to a target output stream
+ *
+ * @param out
* @return
*/
- public static MessagePacker newDefaultPacker(WritableByteChannel channel) {
- return DEFAULT.newPacker(channel);
+ public static MessagePacker newDefaultPacker(OutputStream out)
+ {
+ return new PackerConfig().newPacker(out);
}
/**
- * Create a MessageUnpacker that reads data from then given InputStream, using the default configuration
- * @param in
+ * Create a packer that outputs the packed data to a channel
+ *
+ * @param channel
* @return
*/
- public static MessageUnpacker newDefaultUnpacker(InputStream in) {
- return DEFAULT.newUnpacker(in);
+ public static MessagePacker newDefaultPacker(WritableByteChannel channel)
+ {
+ return new PackerConfig().newPacker(channel);
}
/**
- * Create a MessageUnpacker that reads data from the given channel, using the default configuration
- * @param channel
+ * Create a packer for storing packed data into a byte array
+ *
* @return
*/
- public static MessageUnpacker newDefaultUnpacker(ReadableByteChannel channel) {
- return DEFAULT.newUnpacker(channel);
+ public static MessageBufferPacker newDefaultBufferPacker()
+ {
+ return new PackerConfig().newBufferPacker();
}
/**
- * Create a MessageUnpacker that reads data from the given byte array, using the default configuration
- * @param arr
+ * Create an unpacker that reads the data from a given input
+ *
+ * @param in
* @return
*/
- public static MessageUnpacker newDefaultUnpacker(byte[] arr) {
- return DEFAULT.newUnpacker(arr);
+ public static MessageUnpacker newDefaultUnpacker(MessageBufferInput in)
+ {
+ return new UnpackerConfig().newUnpacker(in);
}
/**
- * Create a MessageUnpacker that reads data form the given byte array [offset, .. offset+length), using the default
- * configuration.
- * @param arr
- * @param offset
- * @param length
+ * Create an unpacker that reads the data from a given input stream
+ *
+ * @param in
* @return
*/
- public static MessageUnpacker newDefaultUnpacker(byte[] arr, int offset, int length) {
- return DEFAULT.newUnpacker(arr, offset, length);
+ public static MessageUnpacker newDefaultUnpacker(InputStream in)
+ {
+ return new UnpackerConfig().newUnpacker(in);
}
-
/**
- * Create a MessagePacker that outputs the packed data to the specified stream
- * @param out
+ * Create an unpacker that reads the data from a given channel
+ *
+ * @param channel
+ * @return
*/
- public MessagePacker newPacker(OutputStream out) {
- return new MessagePacker(new OutputStreamBufferOutput(out), config);
+ public static MessageUnpacker newDefaultUnpacker(ReadableByteChannel channel)
+ {
+ return new UnpackerConfig().newUnpacker(channel);
}
/**
- * Create a MessagePacker that outputs the packed data to the specified channel
- * @param channel
+ * Create an unpacker that reads the data from a given byte array
+ *
+ * @param contents
+ * @return
*/
- public MessagePacker newPacker(WritableByteChannel channel) {
- return new MessagePacker(new ChannelBufferOutput(channel), config);
+ public static MessageUnpacker newDefaultUnpacker(byte[] contents)
+ {
+ return new UnpackerConfig().newUnpacker(contents);
}
/**
- * Create a MessageUnpacker that reads data from the given InputStream.
- * For reading data efficiently from byte[], use {@link MessageUnpacker(byte[])} or {@link MessageUnpacker(byte[], int, int)} instead of this constructor.
+ * Create an unpacker that reads the data from a given byte array [offset, offset+length)
*
- * @param in
+ * @param contents
+ * @param offset
+ * @param length
+ * @return
*/
- public MessageUnpacker newUnpacker(InputStream in) {
- return new MessageUnpacker(InputStreamBufferInput.newBufferInput(in), config);
+ public static MessageUnpacker newDefaultUnpacker(byte[] contents, int offset, int length)
+ {
+ return new UnpackerConfig().newUnpacker(contents, offset, length);
}
/**
- * Create a MessageUnpacker that reads data from the given ReadableByteChannel.
- * @param in
+ * MessagePacker configuration.
*/
- public MessageUnpacker newUnpacker(ReadableByteChannel in) {
- return new MessageUnpacker(new ChannelBufferInput(in), config);
- }
+ public static class PackerConfig
+ {
+ /**
+ * Use String.getBytes() for converting Java Strings that are smaller than this threshold into UTF8.
+ * Note that this parameter is subject to change.
+ */
+ public int smallStringOptimizationThreshold = 512;
+ /**
+ * When the next payload size exceeds this threshold, MessagePacker will call MessageBufferOutput.flush() before
+ * packing the data.
+ */
+ public int bufferFlushThreshold = 8192;
- /**
- * Create a MessageUnpacker that reads data from the given byte array.
- *
- * @param arr
- */
- public MessageUnpacker newUnpacker(byte[] arr) {
- return new MessageUnpacker(new ArrayBufferInput(arr), config);
+ /**
+ * Create a packer that outputs the packed data to a given output
+ *
+ * @param out
+ * @return
+ */
+ public MessagePacker newPacker(MessageBufferOutput out)
+ {
+ return new MessagePacker(out, this);
+ }
+
+ /**
+ * Create a packer that outputs the packed data to a given output stream
+ *
+ * @param out
+ * @return
+ */
+ public MessagePacker newPacker(OutputStream out)
+ {
+ return newPacker(new OutputStreamBufferOutput(out));
+ }
+
+ /**
+ * Create a packer that outputs the packed data to a given output channel
+ *
+ * @param channel
+ * @return
+ */
+ public MessagePacker newPacker(WritableByteChannel channel)
+ {
+ return newPacker(new ChannelBufferOutput(channel));
+ }
+
+ /**
+ * Create a packer for storing packed data into a byte array
+ *
+ * @return
+ */
+ public MessageBufferPacker newBufferPacker()
+ {
+ return new MessageBufferPacker(this);
+ }
}
/**
- * Create a MessageUnpacker that reads data from the given byte array [offset, offset+length)
- * @param arr
- * @param offset
- * @param length
+ * MessageUnpacker configuration.
*/
- public MessageUnpacker newUnpacker(byte[] arr, int offset, int length) {
- return new MessageUnpacker(new ArrayBufferInput(arr, offset, length), config);
- }
+ public static class UnpackerConfig
+ {
+ /**
+ * Allow unpackBinaryHeader to read str format family (default:true)
+ */
+ public boolean allowReadingStringAsBinary = true;
+ /**
+ * Allow unpackRawStringHeader and unpackString to read bin format family (default: true)
+ */
+ public boolean allowReadingBinaryAsString = true;
+ /**
+ * Action when encountered a malformed input
+ */
+ public CodingErrorAction actionOnMalformedString = CodingErrorAction.REPLACE;
+ /**
+ * Action when an unmappable character is found
+ */
+ public CodingErrorAction actionOnUnmappableString = CodingErrorAction.REPLACE;
+ /**
+ * unpackString size limit. (default: Integer.MAX_VALUE)
+ */
+ public int stringSizeLimit = Integer.MAX_VALUE;
+
+ /**
+ *
+ */
+ public int stringDecoderBufferSize = 8192;
+
+ /**
+ * Create an unpacker that reads the data from a given input
+ *
+ * @param in
+ * @return
+ */
+ public MessageUnpacker newUnpacker(MessageBufferInput in)
+ {
+ return new MessageUnpacker(in, this);
+ }
+
+ /**
+ * Create an unpacker that reads the data from a given input stream
+ *
+ * @param in
+ * @return
+ */
+ public MessageUnpacker newUnpacker(InputStream in)
+ {
+ return newUnpacker(new InputStreamBufferInput(in));
+ }
+
+ /**
+ * Create an unpacker that reads the data from a given channel
+ *
+ * @param channel
+ * @return
+ */
+ public MessageUnpacker newUnpacker(ReadableByteChannel channel)
+ {
+ return newUnpacker(new ChannelBufferInput(channel));
+ }
+
+ /**
+ * Create an unpacker that reads the data from a given byte array
+ *
+ * @param contents
+ * @return
+ */
+ public MessageUnpacker newUnpacker(byte[] contents)
+ {
+ return newUnpacker(new ArrayBufferInput(contents));
+ }
+
+ /**
+ * Create an unpacker that reads the data from a given byte array [offset, offset+size)
+ *
+ * @param contents
+ * @return
+ */
+ public MessageUnpacker newUnpacker(byte[] contents, int offset, int length)
+ {
+ return newUnpacker(new ArrayBufferInput(contents, offset, length));
+ }
+ }
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessagePackException.java b/msgpack-core/src/main/java/org/msgpack/core/MessagePackException.java
index 4359f8f32..79474b4b7 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessagePackException.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessagePackException.java
@@ -15,31 +15,36 @@
//
package org.msgpack.core;
-
/**
* A base class of all of the message pack exceptions.
*/
-public class MessagePackException extends RuntimeException {
- public MessagePackException() {
+public class MessagePackException
+ extends RuntimeException
+{
+ public MessagePackException()
+ {
super();
}
- public MessagePackException(String message) {
+ public MessagePackException(String message)
+ {
super(message);
}
- public MessagePackException(String message, Throwable cause) {
+ public MessagePackException(String message, Throwable cause)
+ {
super(message, cause);
}
- public MessagePackException(Throwable cause) {
+ public MessagePackException(Throwable cause)
+ {
super(cause);
}
-
- public static UnsupportedOperationException UNSUPPORTED(String operationName) {
+ public static UnsupportedOperationException UNSUPPORTED(String operationName)
+ {
return new UnsupportedOperationException(operationName);
}
- public static IllegalStateException UNREACHABLE = new IllegalStateException("Cannot reach here");
+ public static final IllegalStateException UNREACHABLE = new IllegalStateException("Cannot reach here");
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessagePacker.java b/msgpack-core/src/main/java/org/msgpack/core/MessagePacker.java
index 280ef0e70..c99653fc9 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessagePacker.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessagePacker.java
@@ -20,27 +20,59 @@
import org.msgpack.value.Value;
import java.io.Closeable;
-import java.math.BigInteger;
import java.io.IOException;
+import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
-import java.nio.charset.CodingErrorAction;
-import static org.msgpack.core.MessagePack.Code.*;
-import static org.msgpack.core.Preconditions.*;
+import static org.msgpack.core.MessagePack.Code.ARRAY16;
+import static org.msgpack.core.MessagePack.Code.ARRAY32;
+import static org.msgpack.core.MessagePack.Code.BIN16;
+import static org.msgpack.core.MessagePack.Code.BIN32;
+import static org.msgpack.core.MessagePack.Code.BIN8;
+import static org.msgpack.core.MessagePack.Code.EXT16;
+import static org.msgpack.core.MessagePack.Code.EXT32;
+import static org.msgpack.core.MessagePack.Code.EXT8;
+import static org.msgpack.core.MessagePack.Code.FALSE;
+import static org.msgpack.core.MessagePack.Code.FIXARRAY_PREFIX;
+import static org.msgpack.core.MessagePack.Code.FIXEXT1;
+import static org.msgpack.core.MessagePack.Code.FIXEXT16;
+import static org.msgpack.core.MessagePack.Code.FIXEXT2;
+import static org.msgpack.core.MessagePack.Code.FIXEXT4;
+import static org.msgpack.core.MessagePack.Code.FIXEXT8;
+import static org.msgpack.core.MessagePack.Code.FIXMAP_PREFIX;
+import static org.msgpack.core.MessagePack.Code.FIXSTR_PREFIX;
+import static org.msgpack.core.MessagePack.Code.FLOAT32;
+import static org.msgpack.core.MessagePack.Code.FLOAT64;
+import static org.msgpack.core.MessagePack.Code.INT16;
+import static org.msgpack.core.MessagePack.Code.INT32;
+import static org.msgpack.core.MessagePack.Code.INT64;
+import static org.msgpack.core.MessagePack.Code.INT8;
+import static org.msgpack.core.MessagePack.Code.MAP16;
+import static org.msgpack.core.MessagePack.Code.MAP32;
+import static org.msgpack.core.MessagePack.Code.NIL;
+import static org.msgpack.core.MessagePack.Code.STR16;
+import static org.msgpack.core.MessagePack.Code.STR32;
+import static org.msgpack.core.MessagePack.Code.STR8;
+import static org.msgpack.core.MessagePack.Code.TRUE;
+import static org.msgpack.core.MessagePack.Code.UINT16;
+import static org.msgpack.core.MessagePack.Code.UINT32;
+import static org.msgpack.core.MessagePack.Code.UINT64;
+import static org.msgpack.core.MessagePack.Code.UINT8;
+import static org.msgpack.core.Preconditions.checkNotNull;
/**
* Writer of message packed data.
- *
+ *
*
* MessagePacker provides packXXX methods for writing values in the message pack format.
* To write raw string or binary data, first use packRawStringHeader or packBinaryHeader to specify the data length,
* then call writePayload(...) method.
*
- *
+ *
*
* MessagePacker class has no guarantee to produce the correct message-pack format data if it is not used correctly:
* packXXX methods of primitive values always produce the correct format, but
@@ -48,87 +80,86 @@
* packRawStringHeader(length) and packBinaryHeader(length) must be followed by writePayload( ... length) to supply
* the binary data of the specified length in the header.
*
- *
*/
-public class MessagePacker implements Closeable {
+public class MessagePacker
+ implements Closeable
+{
+ private final int smallStringOptimizationThreshold;
+
+ private final int bufferFlushThreshold;
- private final MessagePack.Config config;
+ protected MessageBufferOutput out;
- private MessageBufferOutput out;
private MessageBuffer buffer;
- private MessageBuffer strLenBuffer;
private int position;
+ /**
+ * Total written byte size
+ */
+ private long totalFlushBytes;
+
/**
* String encoder
*/
private CharsetEncoder encoder;
-
/**
* Create an MessagePacker that outputs the packed data to the given {@link org.msgpack.core.buffer.MessageBufferOutput}
*
* @param out MessageBufferOutput. Use {@link org.msgpack.core.buffer.OutputStreamBufferOutput}, {@link org.msgpack.core.buffer.ChannelBufferOutput} or
- * your own implementation of {@link org.msgpack.core.buffer.MessageBufferOutput} interface.
- *
+ * your own implementation of {@link org.msgpack.core.buffer.MessageBufferOutput} interface.
*/
- public MessagePacker(MessageBufferOutput out) {
- this(out, MessagePack.DEFAULT_CONFIG);
- }
-
- public MessagePacker(MessageBufferOutput out, MessagePack.Config config) {
- this.config = checkNotNull(config, "config is null");
+ public MessagePacker(MessageBufferOutput out, MessagePack.PackerConfig config)
+ {
this.out = checkNotNull(out, "MessageBufferOutput is null");
+ // We must copy the configuration parameters here since the config object is mutable
+ this.smallStringOptimizationThreshold = config.smallStringOptimizationThreshold;
+ this.bufferFlushThreshold = config.bufferFlushThreshold;
this.position = 0;
+ this.totalFlushBytes = 0;
}
- public void reset(MessageBufferOutput out) throws IOException {
+ /**
+ * Reset output. This method doesn't close the old resource.
+ *
+ * @param out new output
+ * @return the old resource
+ */
+ public MessageBufferOutput reset(MessageBufferOutput out)
+ throws IOException
+ {
// Validate the argument
MessageBufferOutput newOut = checkNotNull(out, "MessageBufferOutput is null");
- try {
- if(this.out != newOut) {
- this.out.close();
- }
- }
- finally {
- // Reset the internal states here for the exception safety
- this.out = newOut;
- this.position = 0;
- }
- }
+ // Flush before reset
+ flush();
+ MessageBufferOutput old = this.out;
+ this.out = newOut;
+ // Reset totalFlushBytes
+ this.totalFlushBytes = 0;
- private void prepareEncoder() {
- if(encoder == null) {
- this.encoder = MessagePack.UTF8.newEncoder().onMalformedInput(config.getActionOnMalFormedInput()).onUnmappableCharacter(config.getActionOnMalFormedInput());
- }
+ return old;
}
- private void prepareBuffer() throws IOException {
- if(buffer == null) {
- buffer = out.next(config.getPackerBufferSize());
- }
+ public long getTotalWrittenBytes()
+ {
+ return totalFlushBytes + position;
}
-
- public void flush() throws IOException {
- if(buffer == null) {
- return;
- }
-
- if(position == buffer.size()) {
- out.flush(buffer);
+ public void flush()
+ throws IOException
+ {
+ if (position > 0) {
+ flushBuffer();
}
- else {
- out.flush(buffer.slice(0, position));
- }
- buffer = null;
- position = 0;
+ out.flush();
}
- public void close() throws IOException {
+ public void close()
+ throws IOException
+ {
try {
flush();
}
@@ -137,110 +168,153 @@ public void close() throws IOException {
}
}
- private void ensureCapacity(int numBytesToWrite) throws IOException {
- if(buffer == null || position + numBytesToWrite >= buffer.size()) {
- flush();
- buffer = out.next(Math.max(config.getPackerBufferSize(), numBytesToWrite));
- }
+ private void flushBuffer()
+ throws IOException
+ {
+ out.writeBuffer(position);
+ buffer = null;
+ totalFlushBytes += position;
+ position = 0;
}
+ private void ensureCapacity(int minimumSize)
+ throws IOException
+ {
+ if (buffer == null) {
+ buffer = out.next(minimumSize);
+ }
+ else if (position + minimumSize >= buffer.size()) {
+ flushBuffer();
+ buffer = out.next(minimumSize);
+ }
+ }
- private void writeByte(byte b) throws IOException {
+ private void writeByte(byte b)
+ throws IOException
+ {
ensureCapacity(1);
buffer.putByte(position++, b);
}
-
- private void writeByteAndByte(byte b, byte v) throws IOException {
+ private void writeByteAndByte(byte b, byte v)
+ throws IOException
+ {
ensureCapacity(2);
buffer.putByte(position++, b);
buffer.putByte(position++, v);
}
- private void writeByteAndShort(byte b, short v) throws IOException {
+ private void writeByteAndShort(byte b, short v)
+ throws IOException
+ {
ensureCapacity(3);
buffer.putByte(position++, b);
buffer.putShort(position, v);
position += 2;
}
- private void writeByteAndInt(byte b, int v) throws IOException {
+ private void writeByteAndInt(byte b, int v)
+ throws IOException
+ {
ensureCapacity(5);
buffer.putByte(position++, b);
buffer.putInt(position, v);
position += 4;
}
- private void writeByteAndFloat(byte b, float v) throws IOException {
+ private void writeByteAndFloat(byte b, float v)
+ throws IOException
+ {
ensureCapacity(5);
buffer.putByte(position++, b);
buffer.putFloat(position, v);
position += 4;
}
- private void writeByteAndDouble(byte b, double v) throws IOException {
+ private void writeByteAndDouble(byte b, double v)
+ throws IOException
+ {
ensureCapacity(9);
buffer.putByte(position++, b);
buffer.putDouble(position, v);
position += 8;
}
- private void writeByteAndLong(byte b, long v) throws IOException {
+ private void writeByteAndLong(byte b, long v)
+ throws IOException
+ {
ensureCapacity(9);
buffer.putByte(position++, b);
buffer.putLong(position, v);
position += 8;
}
- private void writeShort(short v) throws IOException {
+ private void writeShort(short v)
+ throws IOException
+ {
ensureCapacity(2);
buffer.putShort(position, v);
position += 2;
}
- private void writeInt(int v) throws IOException {
+ private void writeInt(int v)
+ throws IOException
+ {
ensureCapacity(4);
buffer.putInt(position, v);
position += 4;
}
- private void writeLong(long v) throws IOException {
+ private void writeLong(long v)
+ throws IOException
+ {
ensureCapacity(8);
buffer.putLong(position, v);
position += 8;
}
- public MessagePacker packNil() throws IOException {
+ public MessagePacker packNil()
+ throws IOException
+ {
writeByte(NIL);
return this;
}
- public MessagePacker packBoolean(boolean b) throws IOException {
+ public MessagePacker packBoolean(boolean b)
+ throws IOException
+ {
writeByte(b ? TRUE : FALSE);
return this;
}
-
- public MessagePacker packByte(byte b) throws IOException {
- if(b < -(1 << 5)) {
+ public MessagePacker packByte(byte b)
+ throws IOException
+ {
+ if (b < -(1 << 5)) {
writeByteAndByte(INT8, b);
- } else {
+ }
+ else {
writeByte(b);
}
return this;
}
- public MessagePacker packShort(short v) throws IOException {
- if(v < -(1 << 5)) {
- if(v < -(1 << 7)) {
+ public MessagePacker packShort(short v)
+ throws IOException
+ {
+ if (v < -(1 << 5)) {
+ if (v < -(1 << 7)) {
writeByteAndShort(INT16, v);
- } else {
+ }
+ else {
writeByteAndByte(INT8, (byte) v);
}
- } else if(v < (1 << 7)) {
+ }
+ else if (v < (1 << 7)) {
writeByte((byte) v);
- } else {
- if(v < (1 << 8)) {
+ }
+ else {
+ if (v < (1 << 8)) {
writeByteAndByte(UINT8, (byte) v);
}
else {
@@ -250,23 +324,31 @@ public MessagePacker packShort(short v) throws IOException {
return this;
}
- public MessagePacker packInt(int r) throws IOException {
+ public MessagePacker packInt(int r)
+ throws IOException
+ {
if (r < -(1 << 5)) {
if (r < -(1 << 15)) {
writeByteAndInt(INT32, r);
- } else if (r < -(1 << 7)) {
+ }
+ else if (r < -(1 << 7)) {
writeByteAndShort(INT16, (short) r);
- } else {
+ }
+ else {
writeByteAndByte(INT8, (byte) r);
}
- } else if (r < (1 << 7)) {
+ }
+ else if (r < (1 << 7)) {
writeByte((byte) r);
- } else {
+ }
+ else {
if (r < (1 << 8)) {
writeByteAndByte(UINT8, (byte) r);
- } else if (r < (1 << 16)) {
+ }
+ else if (r < (1 << 16)) {
writeByteAndShort(UINT16, (short) r);
- } else {
+ }
+ else {
// unsigned 32
writeByteAndInt(UINT32, r);
}
@@ -274,35 +356,45 @@ public MessagePacker packInt(int r) throws IOException {
return this;
}
- public MessagePacker packLong(long v) throws IOException {
+ public MessagePacker packLong(long v)
+ throws IOException
+ {
if (v < -(1L << 5)) {
if (v < -(1L << 15)) {
if (v < -(1L << 31)) {
writeByteAndLong(INT64, v);
- } else {
+ }
+ else {
writeByteAndInt(INT32, (int) v);
}
- } else {
+ }
+ else {
if (v < -(1 << 7)) {
writeByteAndShort(INT16, (short) v);
- } else {
+ }
+ else {
writeByteAndByte(INT8, (byte) v);
}
}
- } else if (v < (1 << 7)) {
+ }
+ else if (v < (1 << 7)) {
// fixnum
writeByte((byte) v);
- } else {
+ }
+ else {
if (v < (1L << 16)) {
if (v < (1 << 8)) {
writeByteAndByte(UINT8, (byte) v);
- } else {
+ }
+ else {
writeByteAndShort(UINT16, (short) v);
}
- } else {
+ }
+ else {
if (v < (1L << 32)) {
writeByteAndInt(UINT32, (int) v);
- } else {
+ }
+ else {
writeByteAndLong(UINT64, v);
}
}
@@ -310,27 +402,75 @@ public MessagePacker packLong(long v) throws IOException {
return this;
}
- public MessagePacker packBigInteger(BigInteger bi) throws IOException {
- if(bi.bitLength() <= 63) {
+ public MessagePacker packBigInteger(BigInteger bi)
+ throws IOException
+ {
+ if (bi.bitLength() <= 63) {
packLong(bi.longValue());
- } else if(bi.bitLength() == 64 && bi.signum() == 1) {
+ }
+ else if (bi.bitLength() == 64 && bi.signum() == 1) {
writeByteAndLong(UINT64, bi.longValue());
- } else {
- throw new IllegalArgumentException("Messagepack cannot serialize BigInteger larger than 2^64-1");
+ }
+ else {
+ throw new IllegalArgumentException("MessagePack cannot serialize BigInteger larger than 2^64-1");
}
return this;
}
- public MessagePacker packFloat(float v) throws IOException {
+ public MessagePacker packFloat(float v)
+ throws IOException
+ {
writeByteAndFloat(FLOAT32, v);
return this;
}
- public MessagePacker packDouble(double v) throws IOException {
+ public MessagePacker packDouble(double v)
+ throws IOException
+ {
writeByteAndDouble(FLOAT64, v);
return this;
}
+ private void packStringWithGetBytes(String s)
+ throws IOException
+ {
+ // JVM performs various optimizations (memory allocation, reusing encoder etc.) when String.getBytes is used
+ byte[] bytes = s.getBytes(MessagePack.UTF8);
+ // Write the length and payload of small string to the buffer so that it avoids an extra flush of buffer
+ packRawStringHeader(bytes.length);
+ addPayload(bytes);
+ }
+
+ private void prepareEncoder()
+ {
+ if (encoder == null) {
+ this.encoder = MessagePack.UTF8.newEncoder();
+ }
+ }
+
+ private int encodeStringToBufferAt(int pos, String s)
+ {
+ prepareEncoder();
+ ByteBuffer bb = buffer.sliceAsByteBuffer(pos, buffer.size() - pos);
+ int startPosition = bb.position();
+ CharBuffer in = CharBuffer.wrap(s);
+ CoderResult cr = encoder.encode(in, bb, true);
+ if (cr.isError()) {
+ try {
+ cr.throwException();
+ }
+ catch (CharacterCodingException e) {
+ throw new MessageStringCodingException(e);
+ }
+ }
+ if (cr.isUnderflow() || cr.isOverflow()) {
+ return -1;
+ }
+ return bb.position() - startPosition;
+ }
+
+ private static final int UTF_8_MAX_CHAR_SIZE = 6;
+
/**
* Pack the input String in UTF-8 encoding
*
@@ -338,220 +478,284 @@ public MessagePacker packDouble(double v) throws IOException {
* @return
* @throws IOException
*/
- public MessagePacker packString(String s) throws IOException {
- if(s.length() <= 0) {
+ public MessagePacker packString(String s)
+ throws IOException
+ {
+ if (s.length() <= 0) {
packRawStringHeader(0);
return this;
}
-
- CharBuffer in = CharBuffer.wrap(s);
- prepareEncoder();
-
- flush();
-
- prepareBuffer();
- boolean isExtended = false;
- ByteBuffer encodeBuffer = buffer.toByteBuffer(position, buffer.size()-position);
- encoder.reset();
- while(in.hasRemaining()) {
- try {
- CoderResult cr = encoder.encode(in, encodeBuffer, true);
-
- if(cr.isUnderflow()) {
- cr = encoder.flush(encodeBuffer);
+ else if (s.length() < smallStringOptimizationThreshold) {
+ // Using String.getBytes is generally faster for small strings
+ packStringWithGetBytes(s);
+ return this;
+ }
+ else if (s.length() < (1 << 8)) {
+ // ensure capacity for 2-byte raw string header + the maximum string size (+ 1 byte for falback code)
+ ensureCapacity(2 + s.length() * UTF_8_MAX_CHAR_SIZE + 1);
+ // keep 2-byte header region and write raw string
+ int written = encodeStringToBufferAt(position + 2, s);
+ if (written >= 0) {
+ if (written < (1 << 8)) {
+ buffer.putByte(position++, STR8);
+ buffer.putByte(position++, (byte) written);
+ position += written;
}
-
- if(cr.isOverflow()) {
- // Allocate a larger buffer
- int estimatedRemainingSize = Math.max(1, (int) (in.remaining() * encoder.averageBytesPerChar()));
- encodeBuffer.flip();
- ByteBuffer newBuffer = ByteBuffer.allocate(Math.max((int) (encodeBuffer.capacity() * 1.5), encodeBuffer.remaining() + estimatedRemainingSize));
- newBuffer.put(encodeBuffer);
- encodeBuffer = newBuffer;
- isExtended = true;
- encoder.reset();
- continue;
+ else {
+ if (written >= (1 << 16)) {
+ // this must not happen because s.length() is less than 2^8 and (2^8) * UTF_8_MAX_CHAR_SIZE is less than 2^16
+ throw new IllegalArgumentException("Unexpected UTF-8 encoder state");
+ }
+ // move 1 byte backward to expand 3-byte header region to 3 bytes
+ buffer.putBytes(position + 3,
+ buffer.array(), buffer.arrayOffset() + position + 2, written);
+ // write 3-byte header
+ buffer.putByte(position++, STR16);
+ buffer.putShort(position, (short) written);
+ position += 2;
+ position += written;
}
-
- if(cr.isError()) {
- if((cr.isMalformed() && config.getActionOnMalFormedInput() == CodingErrorAction.REPORT) ||
- (cr.isUnmappable() && config.getActionOnUnmappableCharacter() == CodingErrorAction.REPORT)) {
- cr.throwException();
+ return this;
+ }
+ }
+ else if (s.length() < (1 << 16)) {
+ // ensure capacity for 3-byte raw string header + the maximum string size (+ 2 bytes for fallback code)
+ ensureCapacity(3 + s.length() * UTF_8_MAX_CHAR_SIZE + 2);
+ // keep 3-byte header region and write raw string
+ int written = encodeStringToBufferAt(position + 3, s);
+ if (written >= 0) {
+ if (written < (1 << 16)) {
+ buffer.putByte(position++, STR16);
+ buffer.putShort(position, (short) written);
+ position += 2;
+ position += written;
+ }
+ else {
+ if (written >= (1 << 32)) {
+ // this must not happen because s.length() is less than 2^16 and (2^16) * UTF_8_MAX_CHAR_SIZE is less than 2^32
+ throw new IllegalArgumentException("Unexpected UTF-8 encoder state");
}
+ // move 2 bytes backward to expand 3-byte header region to 5 bytes
+ buffer.putBytes(position + 5,
+ buffer.array(), buffer.arrayOffset() + position + 3, written);
+ // write 3-byte header header
+ buffer.putByte(position++, STR32);
+ buffer.putInt(position, written);
+ position += 4;
+ position += written;
}
- } catch(CharacterCodingException e) {
- throw new MessageStringCodingException(e);
+ return this;
}
}
- encodeBuffer.flip();
- int strLen = encodeBuffer.remaining();
-
- // Preserve the current buffer
- MessageBuffer tmpBuf = buffer;
+ // Here doesn't use above optimized code for s.length() < (1 << 32) so that
+ // ensureCapacity is not called with an integer larger than (3 + ((1 << 16) * UTF_8_MAX_CHAR_SIZE) + 2).
+ // This makes it sure that MessageBufferOutput.next won't be called a size larger than
+ // 384KB, which is OK size to keep in memory.
- // Switch the buffer to write the string length
- if(strLenBuffer == null) {
- strLenBuffer = MessageBuffer.newBuffer(5);
- }
- buffer = strLenBuffer;
- position = 0;
- // pack raw string header (string binary size)
- packRawStringHeader(strLen);
- flush(); // We need to dump the data here to MessageBufferOutput so that we can switch back to the original buffer
-
- // Reset to the original buffer (or encodeBuffer if new buffer is allocated)
- buffer = isExtended ? MessageBuffer.wrap(encodeBuffer) : tmpBuf;
- // No need exists to write payload since the encoded string is already written to the buffer
- position = strLen;
+ // fallback
+ packStringWithGetBytes(s);
return this;
}
- public MessagePacker packArrayHeader(int arraySize) throws IOException {
- if(arraySize < 0)
+ public MessagePacker packArrayHeader(int arraySize)
+ throws IOException
+ {
+ if (arraySize < 0) {
throw new IllegalArgumentException("array size must be >= 0");
+ }
- if(arraySize < (1 << 4)) {
+ if (arraySize < (1 << 4)) {
writeByte((byte) (FIXARRAY_PREFIX | arraySize));
- } else if(arraySize < (1 << 16)) {
+ }
+ else if (arraySize < (1 << 16)) {
writeByteAndShort(ARRAY16, (short) arraySize);
- } else {
+ }
+ else {
writeByteAndInt(ARRAY32, arraySize);
}
return this;
}
- public MessagePacker packMapHeader(int mapSize) throws IOException {
- if(mapSize < 0)
+ public MessagePacker packMapHeader(int mapSize)
+ throws IOException
+ {
+ if (mapSize < 0) {
throw new IllegalArgumentException("map size must be >= 0");
+ }
- if(mapSize < (1 << 4)) {
+ if (mapSize < (1 << 4)) {
writeByte((byte) (FIXMAP_PREFIX | mapSize));
- } else if(mapSize < (1 << 16)) {
+ }
+ else if (mapSize < (1 << 16)) {
writeByteAndShort(MAP16, (short) mapSize);
- } else {
+ }
+ else {
writeByteAndInt(MAP32, mapSize);
}
return this;
}
- public MessagePacker packValue(Value v) throws IOException {
+ public MessagePacker packValue(Value v)
+ throws IOException
+ {
v.writeTo(this);
return this;
}
- public MessagePacker packExtendedTypeHeader(int extType, int payloadLen) throws IOException {
- if(payloadLen < (1 << 8)) {
- if(payloadLen > 0 && (payloadLen & (payloadLen - 1)) == 0) { // check whether dataLen == 2^x
- if(payloadLen == 1) {
- writeByteAndByte(FIXEXT1, (byte) extType);
- } else if(payloadLen == 2){
- writeByteAndByte(FIXEXT2, (byte) extType);
- } else if(payloadLen == 4) {
- writeByteAndByte(FIXEXT4, (byte) extType);
- } else if(payloadLen == 8) {
- writeByteAndByte(FIXEXT8, (byte) extType);
- } else {
- writeByteAndByte(FIXEXT16, (byte) extType);
+ public MessagePacker packExtensionTypeHeader(byte extType, int payloadLen)
+ throws IOException
+ {
+ if (payloadLen < (1 << 8)) {
+ if (payloadLen > 0 && (payloadLen & (payloadLen - 1)) == 0) { // check whether dataLen == 2^x
+ if (payloadLen == 1) {
+ writeByteAndByte(FIXEXT1, extType);
+ }
+ else if (payloadLen == 2) {
+ writeByteAndByte(FIXEXT2, extType);
+ }
+ else if (payloadLen == 4) {
+ writeByteAndByte(FIXEXT4, extType);
+ }
+ else if (payloadLen == 8) {
+ writeByteAndByte(FIXEXT8, extType);
}
- } else {
+ else if (payloadLen == 16) {
+ writeByteAndByte(FIXEXT16, extType);
+ }
+ else {
+ writeByteAndByte(EXT8, (byte) payloadLen);
+ writeByte(extType);
+ }
+ }
+ else {
writeByteAndByte(EXT8, (byte) payloadLen);
- writeByte((byte) extType);
+ writeByte(extType);
}
- } else if(payloadLen < (1 << 16)) {
+ }
+ else if (payloadLen < (1 << 16)) {
writeByteAndShort(EXT16, (short) payloadLen);
- writeByte((byte) extType);
- } else {
+ writeByte(extType);
+ }
+ else {
writeByteAndInt(EXT32, payloadLen);
- writeByte((byte) extType);
+ writeByte(extType);
// TODO support dataLen > 2^31 - 1
}
return this;
}
- public MessagePacker packBinaryHeader(int len) throws IOException {
- if(len < (1 << 8)) {
+ public MessagePacker packBinaryHeader(int len)
+ throws IOException
+ {
+ if (len < (1 << 8)) {
writeByteAndByte(BIN8, (byte) len);
- } else if(len < (1 << 16)) {
+ }
+ else if (len < (1 << 16)) {
writeByteAndShort(BIN16, (short) len);
- } else {
+ }
+ else {
writeByteAndInt(BIN32, len);
}
return this;
}
- public MessagePacker packRawStringHeader(int len) throws IOException {
- if(len < (1 << 5)) {
+ public MessagePacker packRawStringHeader(int len)
+ throws IOException
+ {
+ if (len < (1 << 5)) {
writeByte((byte) (FIXSTR_PREFIX | len));
- } else if(len < (1 << 8)) {
+ }
+ else if (len < (1 << 8)) {
writeByteAndByte(STR8, (byte) len);
- } else if(len < (1 << 16)) {
+ }
+ else if (len < (1 << 16)) {
writeByteAndShort(STR16, (short) len);
- } else {
+ }
+ else {
writeByteAndInt(STR32, len);
}
return this;
}
+ /**
+ * Writes buffer to the output.
+ * This method is used with packRawStringHeader or packBinaryHeader.
+ *
+ * @param src the data to add
+ * @return this
+ * @throws IOException
+ */
+ public MessagePacker writePayload(byte[] src)
+ throws IOException
+ {
+ return writePayload(src, 0, src.length);
+ }
- public MessagePacker writePayload(ByteBuffer src) throws IOException {
- if(src.remaining() >= config.getPackerRawDataCopyingThreshold()) {
- // Use the source ByteBuffer directly to avoid memory copy
-
- // First, flush the current buffer contents
- flush();
-
- // Wrap the input source as a MessageBuffer
- MessageBuffer wrapped = MessageBuffer.wrap(src).slice(src.position(), src.remaining());
- // Then, dump the source data to the output
- out.flush(wrapped);
- src.position(src.limit());
+ /**
+ * Writes buffer to the output.
+ * This method is used with packRawStringHeader or packBinaryHeader.
+ *
+ * @param src the data to add
+ * @param off the start offset in the data
+ * @param len the number of bytes to add
+ * @return this
+ * @throws IOException
+ */
+ public MessagePacker writePayload(byte[] src, int off, int len)
+ throws IOException
+ {
+ if (buffer.size() - position < len || len > bufferFlushThreshold) {
+ flush(); // call flush before write
+ out.write(src, off, len);
+ totalFlushBytes += len;
}
else {
- // If the input source is small, simply copy the contents to the buffer
- while(src.remaining() > 0) {
- if(position >= buffer.size())
- flush();
- int writeLen = Math.min(buffer.size() - position, src.remaining());
- buffer.putByteBuffer(position, src, writeLen);
- position += writeLen;
- }
+ buffer.putBytes(position, src, off, len);
+ position += len;
}
return this;
}
- public MessagePacker writePayload(byte[] src) throws IOException {
- return writePayload(src, 0, src.length);
+ /**
+ * Writes buffer to the output.
+ * Unlike writePayload method, addPayload method doesn't copy the source data. It means that the caller
+ * must not modify the data after calling this method.
+ *
+ * @param src the data to add
+ * @return this
+ * @throws IOException
+ */
+ public MessagePacker addPayload(byte[] src)
+ throws IOException
+ {
+ return addPayload(src, 0, src.length);
}
- public MessagePacker writePayload(byte[] src, int off, int len) throws IOException {
- if(len >= config.getPackerRawDataCopyingThreshold()) {
- // Use the input array directory to avoid memory copy
-
- // Flush the current buffer contents
- flush();
-
- // Wrap the input array as a MessageBuffer
- MessageBuffer wrapped = MessageBuffer.wrap(src).slice(off, len);
- // Dump the source data to the output
- out.flush(wrapped);
+ /**
+ * Writes buffer to the output.
+ * Unlike writePayload method, addPayload method doesn't copy the source data. It means that the caller
+ * must not modify the data after calling this method.
+ *
+ * @param src the data to add
+ * @param off the start offset in the data
+ * @param len the number of bytes to add
+ * @return this
+ * @throws IOException
+ */
+ public MessagePacker addPayload(byte[] src, int off, int len)
+ throws IOException
+ {
+ if (buffer.size() - position < len || len > bufferFlushThreshold) {
+ flush(); // call flush before add
+ out.add(src, off, len);
+ totalFlushBytes += len;
}
else {
- int cursor = 0;
- while(cursor < len) {
- if(buffer != null && position >= buffer.size()) {
- flush();
- }
- prepareBuffer();
- int writeLen = Math.min(buffer.size() - position, len - cursor);
- buffer.putBytes(position, src, off + cursor, writeLen);
- position += writeLen;
- cursor += writeLen;
- }
+ buffer.putBytes(position, src, off, len);
+ position += len;
}
return this;
}
-
-
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageSizeException.java b/msgpack-core/src/main/java/org/msgpack/core/MessageSizeException.java
index 0947a17f8..7636f614c 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessageSizeException.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageSizeException.java
@@ -18,22 +18,25 @@
/**
* Thrown to indicate too large message size (e.g, larger than 2^31-1).
*/
-public class MessageSizeException extends MessagePackException {
+public class MessageSizeException
+ extends MessagePackException
+{
private final long size;
- public MessageSizeException(long size) {
+ public MessageSizeException(long size)
+ {
super();
this.size = size;
}
- public MessageSizeException(String message, long size) {
+ public MessageSizeException(String message, long size)
+ {
super(message);
this.size = size;
}
- public long getSize() {
+ public long getSize()
+ {
return size;
}
-
-
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageStringCodingException.java b/msgpack-core/src/main/java/org/msgpack/core/MessageStringCodingException.java
index fbe38bffe..b4d549c4b 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessageStringCodingException.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageStringCodingException.java
@@ -20,18 +20,22 @@
/**
* Thrown to indicate an error when encoding/decoding a String value
*/
-public class MessageStringCodingException extends MessagePackException {
- public MessageStringCodingException(String message, CharacterCodingException cause) {
+public class MessageStringCodingException
+ extends MessagePackException
+{
+ public MessageStringCodingException(String message, CharacterCodingException cause)
+ {
super(message, cause);
}
- public MessageStringCodingException(CharacterCodingException cause) {
+ public MessageStringCodingException(CharacterCodingException cause)
+ {
super(cause);
}
@Override
- public CharacterCodingException getCause() {
+ public CharacterCodingException getCause()
+ {
return (CharacterCodingException) super.getCause();
}
-
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageTypeCastException.java b/msgpack-core/src/main/java/org/msgpack/core/MessageTypeCastException.java
new file mode 100644
index 000000000..62e7d914e
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageTypeCastException.java
@@ -0,0 +1,40 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core;
+
+public class MessageTypeCastException
+ extends MessageTypeException
+{
+ public MessageTypeCastException()
+ {
+ super();
+ }
+
+ public MessageTypeCastException(String message)
+ {
+ super(message);
+ }
+
+ public MessageTypeCastException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+ public MessageTypeCastException(Throwable cause)
+ {
+ super(cause);
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageTypeException.java b/msgpack-core/src/main/java/org/msgpack/core/MessageTypeException.java
index 925f5fd3f..f457ab82b 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessageTypeException.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageTypeException.java
@@ -18,20 +18,26 @@
/**
* Thrown when a type mismatch error occurs
*/
-public class MessageTypeException extends MessagePackException {
- public MessageTypeException() {
+public class MessageTypeException
+ extends MessagePackException
+{
+ public MessageTypeException()
+ {
super();
}
- public MessageTypeException(String message) {
+ public MessageTypeException(String message)
+ {
super(message);
}
- public MessageTypeException(String message, Throwable cause) {
+ public MessageTypeException(String message, Throwable cause)
+ {
super(message, cause);
}
- public MessageTypeException(Throwable cause) {
+ public MessageTypeException(Throwable cause)
+ {
super(cause);
}
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/MessageUnpacker.java b/msgpack-core/src/main/java/org/msgpack/core/MessageUnpacker.java
index 3fb76d0a1..a7cf970eb 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/MessageUnpacker.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/MessageUnpacker.java
@@ -15,38 +15,37 @@
//
package org.msgpack.core;
+import org.msgpack.core.MessagePack.Code;
+import org.msgpack.core.buffer.MessageBuffer;
+import org.msgpack.core.buffer.MessageBufferInput;
+import org.msgpack.value.ImmutableValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueFactory;
+import org.msgpack.value.Variable;
+
import java.io.Closeable;
-import java.io.EOFException;
import java.io.IOException;
-import java.nio.ByteBuffer;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
-import java.nio.charset.MalformedInputException;
-
-import org.msgpack.core.MessagePack.Code;
-import org.msgpack.core.buffer.MessageBuffer;
-import org.msgpack.core.buffer.MessageBufferInput;
-import org.msgpack.value.Cursor;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.holder.FloatHolder;
-import org.msgpack.value.holder.IntegerHolder;
-import org.msgpack.value.holder.ValueHolder;
-import org.msgpack.value.impl.CursorImpl;
-
-import static org.msgpack.core.Preconditions.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import static org.msgpack.core.Preconditions.checkNotNull;
/**
* MessageUnpacker lets an application read message-packed values from a data stream.
* The application needs to call {@link #getNextFormat()} followed by an appropriate unpackXXX method according to the the returned format type.
- *
+ *
*
*
- * MessageUnpacker unpacker = MessagePackFactory.DEFAULT.newUnpacker(...);
+ * MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(...);
* while(unpacker.hasNext()) {
* MessageFormat f = unpacker.getNextFormat();
* switch(f) {
@@ -61,17 +60,23 @@
* break;
* }
* // ...
- * }
+ * }
* }
*
*
*
*/
-public class MessageUnpacker implements Closeable {
-
- private final static MessageBuffer EMPTY_BUFFER = MessageBuffer.wrap(new byte[0]);
-
- private final MessagePack.Config config;
+public class MessageUnpacker
+ implements Closeable
+{
+ private static final MessageBuffer EMPTY_BUFFER = MessageBuffer.wrap(new byte[0]);
+
+ private final boolean allowReadingStringAsBinary;
+ private final boolean allowReadingBinaryAsString;
+ private final CodingErrorAction actionOnMalformedString;
+ private final CodingErrorAction actionOnUnmappableString;
+ private final int stringSizeLimit;
+ private final int stringDecoderBufferSize;
private MessageBufferInput in;
@@ -79,6 +84,7 @@ public class MessageUnpacker implements Closeable {
* Points to the current buffer to read
*/
private MessageBuffer buffer = EMPTY_BUFFER;
+
/**
* Cursor position in the current buffer
*/
@@ -90,19 +96,20 @@ public class MessageUnpacker implements Closeable {
private long totalReadBytes;
/**
- * For preserving the next buffer to use
+ * An extra buffer for reading a small number value across the input buffer boundary.
+ * At most 8-byte buffer (for readLong used by uint 64 and UTF-8 character decoding) is required.
*/
- private MessageBuffer secondaryBuffer = null;
+ private final MessageBuffer numberBuffer = MessageBuffer.allocate(8);
/**
- * Extra buffer for string data at the buffer boundary. Using 17-byte buffer (for FIXEXT16) is sufficient.
+ * After calling prepareNumberBuffer(), the caller should use this variable to read from the returned MessageBuffer.
*/
- private final MessageBuffer extraBuffer = MessageBuffer.wrap(new byte[24]);
+ private int nextReadPosition;
/**
- * True if no more data is available from the MessageBufferInput
+ * For decoding String in unpackString.
*/
- private boolean reachedEOF = false;
+ private StringBuilder decodeStringBuffer;
/**
* For decoding String in unpackString.
@@ -114,302 +121,228 @@ public class MessageUnpacker implements Closeable {
*/
private CharBuffer decodeBuffer;
-
- /**
- * Get a {@link org.msgpack.value.Cursor} for traversing message-packed values
- * @return
- */
- public Cursor getCursor() {
- return new CursorImpl(this);
- }
-
-
-
/**
* Create an MessageUnpacker that reads data from the given MessageBufferInput
*
* @param in
*/
- public MessageUnpacker(MessageBufferInput in) {
- this(in, MessagePack.DEFAULT_CONFIG);
+ public MessageUnpacker(MessageBufferInput in, MessagePack.UnpackerConfig config)
+ {
+ this.in = checkNotNull(in, "MessageBufferInput is null");
+ // We need to copy the configuration parameters since the config object is mutable
+ this.allowReadingStringAsBinary = config.allowReadingStringAsBinary;
+ this.allowReadingBinaryAsString = config.allowReadingBinaryAsString;
+ this.actionOnMalformedString = config.actionOnMalformedString;
+ this.actionOnUnmappableString = config.actionOnUnmappableString;
+ this.stringSizeLimit = config.stringSizeLimit;
+ this.stringDecoderBufferSize = config.stringDecoderBufferSize;
}
-
/**
- * Create an MessageUnpacker
- * @param in
- * @param config configuration
+ * Reset input. This method doesn't close the old resource.
+ *
+ * @param in new input
+ * @return the old resource
*/
- public MessageUnpacker(MessageBufferInput in, MessagePack.Config config) {
- // Root constructor. All of the constructors must call this constructor.
- this.in = checkNotNull(in, "MessageBufferInput is null");
- this.config = checkNotNull(config, "Config");
- }
-
- public void reset(MessageBufferInput in) throws IOException {
+ public MessageBufferInput reset(MessageBufferInput in)
+ throws IOException
+ {
MessageBufferInput newIn = checkNotNull(in, "MessageBufferInput is null");
- try {
- if(in != newIn) {
- close();
- }
- }
- finally {
- // Reset the internal states here for the exception safety
- this.in = newIn;
- this.buffer = EMPTY_BUFFER;
- this.position = 0;
- this.totalReadBytes = 0;
- this.secondaryBuffer = null;
- this.reachedEOF = false;
- // No need to initialize the already allocated string decoder here since we can reuse it.
- }
- }
+ // Reset the internal states
+ MessageBufferInput old = this.in;
+ this.in = newIn;
+ this.buffer = EMPTY_BUFFER;
+ this.position = 0;
+ this.totalReadBytes = 0;
+ // No need to initialize the already allocated string decoder here since we can reuse it.
- public long getTotalReadBytes() {
- return totalReadBytes + position;
+ return old;
}
- private void prepareDecoder() {
- if(decoder == null) {
- decodeBuffer = CharBuffer.allocate(config.getStringDecoderBufferSize());
- decoder = MessagePack.UTF8.newDecoder()
- .onMalformedInput(config.getActionOnMalFormedInput())
- .onUnmappableCharacter(config.getActionOnUnmappableCharacter());
- }
+ public long getTotalReadBytes()
+ {
+ return totalReadBytes + position;
}
-
/**
- * Relocate the cursor position so that it points to the real buffer
+ * Get the next buffer without changing the position
*
- * @return true if it succeeds to move the cursor, or false if there is no more buffer to read
- * @throws IOException when failed to retrieve next buffer
+ * @return
+ * @throws IOException
*/
- private boolean ensureBuffer() throws IOException {
- while(buffer != null && position >= buffer.size()) {
- // Fetch the next buffer
- int bufferSize = buffer.size();
- position -= bufferSize;
- totalReadBytes += bufferSize;
- buffer = takeNextBuffer();
+ private MessageBuffer getNextBuffer()
+ throws IOException
+ {
+ MessageBuffer next = in.next();
+ if (next == null) {
+ throw new MessageInsufficientBufferException();
}
- return buffer != null;
+ assert (buffer != null);
+ totalReadBytes += buffer.size();
+ return next;
}
- private MessageBuffer takeNextBuffer() throws IOException {
- if(reachedEOF)
- return null;
-
- MessageBuffer nextBuffer = null;
- if(secondaryBuffer == null) {
- nextBuffer = in.next();
- }
- else {
- nextBuffer = secondaryBuffer;
- secondaryBuffer = null;
- }
-
- if(nextBuffer == null) {
- reachedEOF = true;
- }
- return nextBuffer;
+ private void nextBuffer()
+ throws IOException
+ {
+ buffer = getNextBuffer();
+ position = 0;
}
-
/**
- * Ensure that the buffer has the data of at least the specified size.
+ * Returns a short size buffer (upto 8 bytes) to read a number value
*
- * @param byteSizeToRead the data size to be read
- * @return if the buffer can have the data of the specified size returns true, or if the input source reached an EOF, it returns false.
+ * @param readLength
+ * @return
* @throws IOException
+ * @throws MessageInsufficientBufferException If no more buffer can be acquired from the input source for reading the specified data length
*/
- private boolean ensure(int byteSizeToRead) throws IOException {
- if(byteSizeToRead == 0)
- return true;
+ private MessageBuffer prepareNumberBuffer(int readLength)
+ throws IOException
+ {
+ int remaining = buffer.size() - position;
+ if (remaining >= readLength) {
+ // When the data is contained inside the default buffer
+ nextReadPosition = position;
+ position += readLength; // here assumes following buffer.getXxx never throws exception
+ return buffer; // Return the default buffer
+ }
+ else {
+ // When the default buffer doesn't contain the whole length
- if(!ensureBuffer())
- return false;
+ // TODO loop this method until castBuffer is filled
+ MessageBuffer next = getNextBuffer();
- // The buffer contains the data
- if(position + byteSizeToRead <= buffer.size()) {
- // OK
- return true;
- }
+ if (remaining > 0) {
+ // TODO This doesn't work if MessageBuffer is allocated by newDirectBuffer.
+ // Add copy method to MessageBuffer to solve this issue.
- // When the data is at the boundary
- /*
- |---(byte size to read) ----|
- -- current buffer --|
- |--- extra buffer (slice) --|----|
- |-------|---------- secondary buffer (slice) ----------------|
-
- */
-
- // If the byte size to read fits within the extra buffer, use the extraBuffer
- MessageBuffer newBuffer = byteSizeToRead <= extraBuffer.size() ? extraBuffer : MessageBuffer.newBuffer(byteSizeToRead);
-
- // Copy the remaining buffer contents to the new buffer
- int firstHalfSize = buffer.size() - position;
- if(firstHalfSize > 0)
- buffer.copyTo(position, newBuffer, 0, firstHalfSize);
-
- // Read the last half contents from the next buffers
- int cursor = firstHalfSize;
- while(cursor < byteSizeToRead) {
- secondaryBuffer = takeNextBuffer();
- if(secondaryBuffer == null)
- return false; // No more buffer to read
-
- // Copy the contents from the secondary buffer to the new buffer
- int copyLen = Math.min(byteSizeToRead - cursor, secondaryBuffer.size());
- secondaryBuffer.copyTo(0, newBuffer, cursor, copyLen);
-
- // Truncate the copied part from the secondaryBuffer
- secondaryBuffer = copyLen == secondaryBuffer.size() ? null : secondaryBuffer.slice(copyLen, secondaryBuffer.size()-copyLen);
- cursor += copyLen;
- }
+ // Copy the data fragment from the current buffer
+ numberBuffer.putBytes(0, buffer.array(), buffer.arrayOffset() + position, remaining);
+ numberBuffer.putBytes(remaining, next.array(), next.arrayOffset(), readLength - remaining);
- // Replace the current buffer with the new buffer
- totalReadBytes += position;
- buffer = byteSizeToRead == newBuffer.size() ? newBuffer : newBuffer.slice(0, byteSizeToRead);
- position = 0;
+ buffer = next;
+ position = readLength - remaining;
+ nextReadPosition = 0;
- return true;
+ return numberBuffer; // Return the numberBuffer
+ }
+ else {
+ buffer = next;
+ position = readLength;
+ nextReadPosition = 0;
+ return buffer;
+ }
+ }
+ }
+
+ private static int utf8MultibyteCharacterSize(byte firstByte)
+ {
+ return Integer.numberOfLeadingZeros(~(firstByte & 0xff) << 24);
}
/**
* Returns true true if this unpacker has more elements.
* When this returns true, subsequent call to {@link #getNextFormat()} returns an
- * MessageFormat instance. If false, {@link #getNextFormat()} will throw an EOFException.
+ * MessageFormat instance. If false, next {@link #getNextFormat()} call will throw an MessageInsufficientBufferException.
*
* @return true if this unpacker has more elements to read
*/
- public boolean hasNext() throws IOException {
- return ensure(1);
+ public boolean hasNext()
+ throws IOException
+ {
+ while (buffer.size() <= position) {
+ MessageBuffer next = in.next();
+ if (next == null) {
+ return false;
+ }
+ totalReadBytes += buffer.size();
+ buffer = next;
+ position = 0;
+ }
+ return true;
}
/**
* Returns the next MessageFormat type. This method should be called after {@link #hasNext()} returns true.
- * If {@link #hasNext()} returns false, calling this method throws {@link java.io.EOFException}.
- *
+ * If {@link #hasNext()} returns false, calling this method throws {@link MessageInsufficientBufferException}.
+ *
* This method does not proceed the internal cursor.
+ *
* @return the next MessageFormat
* @throws IOException when failed to read the input data.
- * @throws EOFException when the end of file reached, i.e. {@link #hasNext()} == false.
+ * @throws MessageInsufficientBufferException when the end of file reached, i.e. {@link #hasNext()} == false.
*/
- public MessageFormat getNextFormat() throws IOException {
- byte b = lookAhead();
+ public MessageFormat getNextFormat()
+ throws IOException
+ {
+ // makes sure that buffer has at leat 1 byte
+ if (!hasNext()) {
+ throw new MessageInsufficientBufferException();
+ }
+ byte b = buffer.getByte(position);
return MessageFormat.valueOf(b);
}
- /**
- * Look-ahead a byte value at the current cursor position.
- * This method does not proceed the cursor.
- *
- * @return
- * @throws IOException
- */
- private byte lookAhead() throws IOException {
- if(ensure(1))
- return buffer.getByte(position);
- else
- throw new EOFException();
- }
-
-
- /**
- * Get the head byte value and proceeds the cursor by 1
- */
- private byte consume() throws IOException {
- byte b = lookAhead();
- position += 1;
- return b;
- }
-
- /**
- * Proceeds the cursor by the specified byte length
- */
- private void consume(int numBytes) throws IOException {
- assert(numBytes >= 0);
- // If position + numBytes becomes negative, it indicates an overflow from Integer.MAX_VALUE.
- if(position + numBytes < 0) {
- ensureBuffer();
- }
- position += numBytes;
- }
-
/**
* Read a byte value at the cursor and proceed the cursor.
*
* @return
* @throws IOException
*/
- private byte readByte() throws IOException {
- if(!ensure(1)) {
- throw new EOFException("insufficient data length for reading byte value");
+ private byte readByte()
+ throws IOException
+ {
+ if (buffer.size() > position) {
+ byte b = buffer.getByte(position);
+ position++;
+ return b;
}
- byte b = buffer.getByte(position);
- consume(1);
- return b;
- }
-
- private short readShort() throws IOException {
- if(!ensure(2)) {
- throw new EOFException("insufficient data length for reading short value");
+ else {
+ nextBuffer();
+ if (buffer.size() > 0) {
+ byte b = buffer.getByte(0);
+ position = 1;
+ return b;
+ }
+ return readByte();
}
- short s = buffer.getShort(position);
- consume(2);
- return s;
}
- private int readInt() throws IOException {
- if(!ensure(4)) {
- throw new EOFException("insufficient data length for reading int value");
- }
- int i = buffer.getInt(position);
- consume(4);
- return i;
+ private short readShort()
+ throws IOException
+ {
+ MessageBuffer numberBuffer = prepareNumberBuffer(2);
+ return numberBuffer.getShort(nextReadPosition);
}
- private float readFloat() throws IOException {
- if(!ensure(4)) {
- throw new EOFException("insufficient data length for reading float value");
- }
- float f = buffer.getFloat(position);
- consume(4);
- return f;
+ private int readInt()
+ throws IOException
+ {
+ MessageBuffer numberBuffer = prepareNumberBuffer(4);
+ return numberBuffer.getInt(nextReadPosition);
}
- private long readLong() throws IOException {
- if(!ensure(8)) {
- throw new EOFException("insufficient data length for reading long value");
- }
- long l = buffer.getLong(position);
- consume(8);
- return l;
+ private long readLong()
+ throws IOException
+ {
+ MessageBuffer numberBuffer = prepareNumberBuffer(8);
+ return numberBuffer.getLong(nextReadPosition);
}
- private double readDouble() throws IOException {
- if(!ensure(8)) {
- throw new EOFException("insufficient data length for reading double value");
- }
- double d = buffer.getDouble(position);
- consume(8);
- return d;
+ private float readFloat()
+ throws IOException
+ {
+ MessageBuffer numberBuffer = prepareNumberBuffer(4);
+ return numberBuffer.getFloat(nextReadPosition);
}
-
- /**
- * Skip reading the specified number of bytes. Use this method only if you know skipping data is safe.
- * For simply skipping the next value, use {@link #skipValue()}.
- *
- * @param numBytes
- * @throws IOException
- */
- public void skipBytes(int numBytes) throws IOException {
- checkArgument(numBytes >= 0, "skip length must be >= 0: " + numBytes);
- consume(numBytes);
+ private double readDouble()
+ throws IOException
+ {
+ MessageBuffer numberBuffer = prepareNumberBuffer(8);
+ return numberBuffer.getDouble(nextReadPosition);
}
/**
@@ -417,16 +350,14 @@ public void skipBytes(int numBytes) throws IOException {
*
* @throws IOException
*/
- public void skipValue() throws IOException {
+ public void skipValue()
+ throws IOException
+ {
int remainingValues = 1;
- while(remainingValues > 0) {
- if(reachedEOF) {
- throw new EOFException();
- }
-
- MessageFormat f = getNextFormat();
- byte b = consume();
- switch(f) {
+ while (remainingValues > 0) {
+ byte b = readByte();
+ MessageFormat f = MessageFormat.valueOf(b);
+ switch (f) {
case POSFIXINT:
case NEGFIXINT:
case BOOLEAN:
@@ -444,81 +375,77 @@ public void skipValue() throws IOException {
}
case FIXSTR: {
int strLen = b & 0x1f;
- consume(strLen);
+ skipPayload(strLen);
break;
}
case INT8:
case UINT8:
- consume(1);
+ skipPayload(1);
break;
case INT16:
case UINT16:
- consume(2);
+ skipPayload(2);
break;
case INT32:
case UINT32:
case FLOAT32:
- consume(4);
+ skipPayload(4);
break;
case INT64:
case UINT64:
case FLOAT64:
- consume(8);
+ skipPayload(8);
break;
case BIN8:
case STR8:
- consume(readNextLength8());
+ skipPayload(readNextLength8());
break;
case BIN16:
case STR16:
- consume(readNextLength16());
+ skipPayload(readNextLength16());
break;
case BIN32:
case STR32:
- consume(readNextLength32());
+ skipPayload(readNextLength32());
break;
case FIXEXT1:
- consume(2);
+ skipPayload(2);
break;
case FIXEXT2:
- consume(3);
+ skipPayload(3);
break;
case FIXEXT4:
- consume(5);
+ skipPayload(5);
break;
case FIXEXT8:
- consume(9);
+ skipPayload(9);
break;
case FIXEXT16:
- consume(17);
+ skipPayload(17);
break;
case EXT8:
- consume(readNextLength8() + 1);
+ skipPayload(readNextLength8() + 1);
break;
case EXT16:
- consume(readNextLength16() + 1);
+ skipPayload(readNextLength16() + 1);
break;
case EXT32:
- consume(readNextLength32() + 1);
+ skipPayload(readNextLength32() + 1);
break;
case ARRAY16:
remainingValues += readNextLength16();
- consume(2);
break;
case ARRAY32:
remainingValues += readNextLength32();
- consume(4);
break;
case MAP16:
remainingValues += readNextLength16() * 2;
- consume(2);
break;
case MAP32:
remainingValues += readNextLength32() * 2; // TODO check int overflow
- consume(2);
break;
case NEVER_USED:
- throw new MessageFormatException(String.format("unknown code: %02x is found", b));
+ throw new MessageNeverUsedFormatException("Encountered 0xC1 \"NEVER_USED\" byte");
}
remainingValues--;
@@ -533,102 +460,190 @@ public void skipValue() throws IOException {
* @return
* @throws MessageFormatException
*/
- private static MessageTypeException unexpected(String expected, byte b)
- throws MessageTypeException {
- ValueType type = ValueType.valueOf(b);
- return new MessageTypeException(String.format("Expected %s, but got %s (%02x)", expected, type.toTypeName(), b));
+ private static MessagePackException unexpected(String expected, byte b)
+ {
+ MessageFormat format = MessageFormat.valueOf(b);
+ if (format == MessageFormat.NEVER_USED) {
+ return new MessageNeverUsedFormatException(String.format("Expected %s, but encountered 0xC1 \"NEVER_USED\" byte", expected));
+ }
+ else {
+ String name = format.getValueType().name();
+ String typeName = name.substring(0, 1) + name.substring(1).toLowerCase();
+ return new MessageTypeException(String.format("Expected %s, but got %s (%02x)", expected, typeName, b));
+ }
}
- public MessageFormat unpackValue(ValueHolder holder) throws IOException {
+ public ImmutableValue unpackValue()
+ throws IOException
+ {
MessageFormat mf = getNextFormat();
- switch(mf.getValueType()) {
+ switch (mf.getValueType()) {
case NIL:
- unpackNil();
- holder.setNil();
- break;
+ readByte();
+ return ValueFactory.newNil();
case BOOLEAN:
- holder.setBoolean(unpackBoolean());
- break;
- case INTEGER: {
- unpackInteger(holder.getIntegerHolder());
- holder.setToInteger();
- break;
+ return ValueFactory.newBoolean(unpackBoolean());
+ case INTEGER:
+ switch (mf) {
+ case UINT64:
+ return ValueFactory.newInteger(unpackBigInteger());
+ default:
+ return ValueFactory.newInteger(unpackLong());
+ }
+ case FLOAT:
+ return ValueFactory.newFloat(unpackDouble());
+ case STRING: {
+ int length = unpackRawStringHeader();
+ return ValueFactory.newString(readPayload(length));
+ }
+ case BINARY: {
+ int length = unpackBinaryHeader();
+ return ValueFactory.newBinary(readPayload(length));
}
- case FLOAT: {
- unpackFloat(holder.getFloatHolder());
- holder.setToFloat();
- break;
+ case ARRAY: {
+ int size = unpackArrayHeader();
+ Value[] array = new Value[size];
+ for (int i = 0; i < size; i++) {
+ array[i] = unpackValue();
+ }
+ return ValueFactory.newArray(array);
+ }
+ case MAP: {
+ int size = unpackMapHeader();
+ Value[] kvs = new Value[size * 2];
+ for (int i = 0; i < size * 2; ) {
+ kvs[i] = unpackValue();
+ i++;
+ kvs[i] = unpackValue();
+ i++;
+ }
+ return ValueFactory.newMap(kvs);
+ }
+ case EXTENSION: {
+ ExtensionTypeHeader extHeader = unpackExtensionTypeHeader();
+ return ValueFactory.newExtension(extHeader.getType(), readPayload(extHeader.getLength()));
+ }
+ default:
+ throw new MessageNeverUsedFormatException("Unknown value type");
+ }
+ }
+
+ public Variable unpackValue(Variable var)
+ throws IOException
+ {
+ MessageFormat mf = getNextFormat();
+ switch (mf.getValueType()) {
+ case NIL:
+ readByte();
+ var.setNilValue();
+ return var;
+ case BOOLEAN:
+ var.setBooleanValue(unpackBoolean());
+ return var;
+ case INTEGER:
+ switch (mf) {
+ case UINT64:
+ var.setIntegerValue(unpackBigInteger());
+ return var;
+ default:
+ var.setIntegerValue(unpackLong());
+ return var;
+ }
+ case FLOAT:
+ var.setFloatValue(unpackDouble());
+ return var;
+ case STRING: {
+ int length = unpackRawStringHeader();
+ var.setStringValue(readPayload(length));
+ return var;
}
- case STRING:
- int strLen = unpackRawStringHeader();
- holder.setString(readPayloadAsReference(strLen));
- break;
case BINARY: {
- int binaryLen = unpackBinaryHeader();
- holder.setBinary(readPayloadAsReference(binaryLen));
- break;
+ int length = unpackBinaryHeader();
+ var.setBinaryValue(readPayload(length));
+ return var;
+ }
+ case ARRAY: {
+ int size = unpackArrayHeader();
+ List list = new ArrayList(size);
+ for (int i = 0; i < size; i++) {
+ list.add(unpackValue());
+ }
+ var.setArrayValue(list);
+ return var;
+ }
+ case MAP: {
+ int size = unpackMapHeader();
+ Map map = new HashMap();
+ for (int i = 0; i < size; i++) {
+ Value k = unpackValue();
+ Value v = unpackValue();
+ map.put(k, v);
+ }
+ var.setMapValue(map);
+ return var;
+ }
+ case EXTENSION: {
+ ExtensionTypeHeader extHeader = unpackExtensionTypeHeader();
+ var.setExtensionValue(extHeader.getType(), readPayload(extHeader.getLength()));
+ return var;
}
- case ARRAY:
- holder.prepareArrayCursor(this);
- break;
- case MAP:
- holder.prepareMapCursor(this);
- break;
- case EXTENDED:
- ExtendedTypeHeader extHeader = unpackExtendedTypeHeader();
- holder.setExt(extHeader.getType(), readPayloadAsReference(extHeader.getLength()));
- break;
+ default:
+ throw new MessageFormatException("Unknown value type");
}
- return mf;
}
- public Object unpackNil() throws IOException {
- byte b = consume();
- if(b == Code.NIL) {
- return null;
+ public void unpackNil()
+ throws IOException
+ {
+ byte b = readByte();
+ if (b == Code.NIL) {
+ return;
}
throw unexpected("Nil", b);
}
-
- public boolean unpackBoolean() throws IOException {
- byte b = consume();
- if(b == Code.FALSE) {
+ public boolean unpackBoolean()
+ throws IOException
+ {
+ byte b = readByte();
+ if (b == Code.FALSE) {
return false;
- } else if(b == Code.TRUE) {
+ }
+ else if (b == Code.TRUE) {
return true;
}
-
throw unexpected("boolean", b);
}
- public byte unpackByte() throws IOException {
- byte b = consume();
- if(Code.isFixInt(b)) {
+ public byte unpackByte()
+ throws IOException
+ {
+ byte b = readByte();
+ if (Code.isFixInt(b)) {
return b;
}
- switch(b) {
+ switch (b) {
case Code.UINT8: // unsigned int 8
byte u8 = readByte();
- if(u8 < (byte) 0) {
+ if (u8 < (byte) 0) {
throw overflowU8(u8);
}
return u8;
case Code.UINT16: // unsigned int 16
short u16 = readShort();
- if(u16 < 0 || u16 > Byte.MAX_VALUE) {
+ if (u16 < 0 || u16 > Byte.MAX_VALUE) {
throw overflowU16(u16);
}
return (byte) u16;
case Code.UINT32: // unsigned int 32
int u32 = readInt();
- if(u32 < 0 || u32 > Byte.MAX_VALUE) {
+ if (u32 < 0 || u32 > Byte.MAX_VALUE) {
throw overflowU32(u32);
}
return (byte) u32;
case Code.UINT64: // unsigned int 64
long u64 = readLong();
- if(u64 < 0L || u64 > Byte.MAX_VALUE) {
+ if (u64 < 0L || u64 > Byte.MAX_VALUE) {
throw overflowU64(u64);
}
return (byte) u64;
@@ -637,19 +652,19 @@ public byte unpackByte() throws IOException {
return i8;
case Code.INT16: // signed int 16
short i16 = readShort();
- if(i16 < Byte.MIN_VALUE || i16 > Byte.MAX_VALUE) {
+ if (i16 < Byte.MIN_VALUE || i16 > Byte.MAX_VALUE) {
throw overflowI16(i16);
}
return (byte) i16;
case Code.INT32: // signed int 32
int i32 = readInt();
- if(i32 < Byte.MIN_VALUE || i32 > Byte.MAX_VALUE) {
+ if (i32 < Byte.MIN_VALUE || i32 > Byte.MAX_VALUE) {
throw overflowI32(i32);
}
return (byte) i32;
case Code.INT64: // signed int 64
long i64 = readLong();
- if(i64 < Byte.MIN_VALUE || i64 > Byte.MAX_VALUE) {
+ if (i64 < Byte.MIN_VALUE || i64 > Byte.MAX_VALUE) {
throw overflowI64(i64);
}
return (byte) i64;
@@ -657,30 +672,32 @@ public byte unpackByte() throws IOException {
throw unexpected("Integer", b);
}
- public short unpackShort() throws IOException {
- byte b = consume();
- if(Code.isFixInt(b)) {
+ public short unpackShort()
+ throws IOException
+ {
+ byte b = readByte();
+ if (Code.isFixInt(b)) {
return (short) b;
}
- switch(b) {
+ switch (b) {
case Code.UINT8: // unsigned int 8
byte u8 = readByte();
return (short) (u8 & 0xff);
case Code.UINT16: // unsigned int 16
short u16 = readShort();
- if(u16 < (short) 0) {
+ if (u16 < (short) 0) {
throw overflowU16(u16);
}
return u16;
case Code.UINT32: // unsigned int 32
int u32 = readInt();
- if(u32 < 0 || u32 > Short.MAX_VALUE) {
+ if (u32 < 0 || u32 > Short.MAX_VALUE) {
throw overflowU32(u32);
}
return (short) u32;
case Code.UINT64: // unsigned int 64
long u64 = readLong();
- if(u64 < 0L || u64 > Short.MAX_VALUE) {
+ if (u64 < 0L || u64 > Short.MAX_VALUE) {
throw overflowU64(u64);
}
return (short) u64;
@@ -692,27 +709,28 @@ public short unpackShort() throws IOException {
return i16;
case Code.INT32: // signed int 32
int i32 = readInt();
- if(i32 < Short.MIN_VALUE || i32 > Short.MAX_VALUE) {
+ if (i32 < Short.MIN_VALUE || i32 > Short.MAX_VALUE) {
throw overflowI32(i32);
}
return (short) i32;
case Code.INT64: // signed int 64
long i64 = readLong();
- if(i64 < Short.MIN_VALUE || i64 > Short.MAX_VALUE) {
+ if (i64 < Short.MIN_VALUE || i64 > Short.MAX_VALUE) {
throw overflowI64(i64);
}
return (short) i64;
}
throw unexpected("Integer", b);
-
}
- public int unpackInt() throws IOException {
- byte b = consume();
- if(Code.isFixInt(b)) {
+ public int unpackInt()
+ throws IOException
+ {
+ byte b = readByte();
+ if (Code.isFixInt(b)) {
return (int) b;
}
- switch(b) {
+ switch (b) {
case Code.UINT8: // unsigned int 8
byte u8 = readByte();
return u8 & 0xff;
@@ -721,13 +739,13 @@ public int unpackInt() throws IOException {
return u16 & 0xffff;
case Code.UINT32: // unsigned int 32
int u32 = readInt();
- if(u32 < 0) {
+ if (u32 < 0) {
throw overflowU32(u32);
}
return u32;
case Code.UINT64: // unsigned int 64
long u64 = readLong();
- if(u64 < 0L || u64 > (long) Integer.MAX_VALUE) {
+ if (u64 < 0L || u64 > (long) Integer.MAX_VALUE) {
throw overflowU64(u64);
}
return (int) u64;
@@ -742,21 +760,22 @@ public int unpackInt() throws IOException {
return i32;
case Code.INT64: // signed int 64
long i64 = readLong();
- if(i64 < (long) Integer.MIN_VALUE || i64 > (long) Integer.MAX_VALUE) {
+ if (i64 < (long) Integer.MIN_VALUE || i64 > (long) Integer.MAX_VALUE) {
throw overflowI64(i64);
}
return (int) i64;
}
throw unexpected("Integer", b);
-
}
- public long unpackLong() throws IOException {
- byte b = consume();
- if(Code.isFixInt(b)) {
+ public long unpackLong()
+ throws IOException
+ {
+ byte b = readByte();
+ if (Code.isFixInt(b)) {
return (long) b;
}
- switch(b) {
+ switch (b) {
case Code.UINT8: // unsigned int 8
byte u8 = readByte();
return (long) (u8 & 0xff);
@@ -765,14 +784,15 @@ public long unpackLong() throws IOException {
return (long) (u16 & 0xffff);
case Code.UINT32: // unsigned int 32
int u32 = readInt();
- if(u32 < 0) {
+ if (u32 < 0) {
return (long) (u32 & 0x7fffffff) + 0x80000000L;
- } else {
+ }
+ else {
return (long) u32;
}
case Code.UINT64: // unsigned int 64
long u64 = readLong();
- if(u64 < 0L) {
+ if (u64 < 0L) {
throw overflowU64(u64);
}
return u64;
@@ -790,15 +810,16 @@ public long unpackLong() throws IOException {
return i64;
}
throw unexpected("Integer", b);
-
}
- public BigInteger unpackBigInteger() throws IOException {
- byte b = consume();
- if(Code.isFixInt(b)) {
+ public BigInteger unpackBigInteger()
+ throws IOException
+ {
+ byte b = readByte();
+ if (Code.isFixInt(b)) {
return BigInteger.valueOf((long) b);
}
- switch(b) {
+ switch (b) {
case Code.UINT8: // unsigned int 8
byte u8 = readByte();
return BigInteger.valueOf((long) (u8 & 0xff));
@@ -807,17 +828,19 @@ public BigInteger unpackBigInteger() throws IOException {
return BigInteger.valueOf((long) (u16 & 0xffff));
case Code.UINT32: // unsigned int 32
int u32 = readInt();
- if(u32 < 0) {
+ if (u32 < 0) {
return BigInteger.valueOf((long) (u32 & 0x7fffffff) + 0x80000000L);
- } else {
+ }
+ else {
return BigInteger.valueOf((long) u32);
}
case Code.UINT64: // unsigned int 64
long u64 = readLong();
- if(u64 < 0L) {
+ if (u64 < 0L) {
BigInteger bi = BigInteger.valueOf(u64 + Long.MAX_VALUE + 1L).setBit(63);
return bi;
- } else {
+ }
+ else {
return BigInteger.valueOf(u64);
}
case Code.INT8: // signed int 8
@@ -836,76 +859,11 @@ public BigInteger unpackBigInteger() throws IOException {
throw unexpected("Integer", b);
}
-
- /**
- * Unpack an integer, then store the read value to the given holder
- * @param holder an integer holder to which the unpacked integer will be set.
- * @throws IOException
- */
- public void unpackInteger(IntegerHolder holder) throws IOException {
- byte b = consume();
-
- if(Code.isFixInt(b)) {
- holder.setByte(b);
- return;
- }
-
- switch(b) {
- case Code.INT8: // signed int 8
- holder.setByte(readByte());
- break;
- case Code.INT16:
- holder.setShort(readShort());
- break;
- case Code.INT32:
- holder.setInt(readInt());
- break;
- case Code.INT64: // signed int 64
- holder.setLong(readLong());
- break;
- case Code.UINT8: // unsigned int 8
- byte u8 = readByte();
- if(u8 < 0) {
- holder.setShort((short) (u8 & 0xFF));
- }
- else {
- holder.setByte(u8);
- }
- break;
- case Code.UINT16: // unsigned int 16
- short u16 = readShort();
- if(u16 < 0) {
- holder.setInt(u16 & 0xFFFF);
- }
- else {
- holder.setShort(u16);
- }
- break;
- case Code.UINT32: // unsigned int 32
- int u32 = readInt();
- if(u32 < 0) {
- holder.setLong((long) (u32 & 0x7fffffff) + 0x80000000L);
- } else {
- holder.setInt(u32);
- }
- break;
- case Code.UINT64: // unsigned int 64
- long u64 = readLong();
- if(u64 < 0L) {
- holder.setBigInteger(BigInteger.valueOf(u64 + Long.MAX_VALUE + 1L).setBit(63));
- } else {
- holder.setLong(u64);
- }
- break;
- default:
- throw unexpected("Integer", b);
- }
- }
-
-
- public float unpackFloat() throws IOException {
- byte b = consume();
- switch(b) {
+ public float unpackFloat()
+ throws IOException
+ {
+ byte b = readByte();
+ switch (b) {
case Code.FLOAT32: // float
float fv = readFloat();
return fv;
@@ -916,9 +874,11 @@ public float unpackFloat() throws IOException {
throw unexpected("Float", b);
}
- public double unpackDouble() throws IOException {
- byte b = consume();
- switch(b) {
+ public double unpackDouble()
+ throws IOException
+ {
+ byte b = readByte();
+ switch (b) {
case Code.FLOAT32: // float
float fv = readFloat();
return (double) fv;
@@ -929,164 +889,249 @@ public double unpackDouble() throws IOException {
throw unexpected("Float", b);
}
- public void unpackFloat(ValueHolder holder) throws IOException {
- unpackFloat(holder.getFloatHolder());
- }
+ private static final String EMPTY_STRING = "";
- public void unpackFloat(FloatHolder holder) throws IOException {
- byte b = consume();
- switch(b) {
- case Code.FLOAT32: // float
- float fv = readFloat();
- holder.setFloat(fv);
- break;
- case Code.FLOAT64: // double
- double dv = readDouble();
- holder.setDouble(dv);
- break;
- default:
- throw unexpected("Float", b);
+ private void resetDecoder()
+ {
+ if (decoder == null) {
+ decodeBuffer = CharBuffer.allocate(stringDecoderBufferSize);
+ decoder = MessagePack.UTF8.newDecoder()
+ .onMalformedInput(actionOnMalformedString)
+ .onUnmappableCharacter(actionOnUnmappableString);
+ }
+ else {
+ decoder.reset();
+ }
+ if (decodeStringBuffer == null) {
+ decodeStringBuffer = new StringBuilder();
+ }
+ else {
+ decodeStringBuffer.setLength(0);
}
}
+ public String unpackString()
+ throws IOException
+ {
+ int len = unpackRawStringHeader();
+ if (len == 0) {
+ return EMPTY_STRING;
+ }
+ if (len > stringSizeLimit) {
+ throw new MessageSizeException(String.format("cannot unpack a String of size larger than %,d: %,d", stringSizeLimit, len), len);
+ }
+ if (buffer.size() - position >= len) {
+ return decodeStringFastPath(len);
+ }
- private final static String EMPTY_STRING = "";
-
- public String unpackString() throws IOException {
- int strLen = unpackRawStringHeader();
- if(strLen > 0) {
- if(strLen > config.getMaxUnpackStringSize()) {
- throw new MessageSizeException(String.format("cannot unpack a String of size larger than %,d: %,d", config.getMaxUnpackStringSize(), strLen), strLen);
- }
-
- prepareDecoder();
- assert(decoder != null);
-
- decoder.reset();
-
- try {
- int cursor = 0;
- decodeBuffer.clear();
- StringBuilder sb = new StringBuilder();
- while(cursor < strLen) {
- if (!ensure(strLen))
- throw new EOFException();
-
- int readLen = Math.min(buffer.size() - position, strLen-cursor);
- ByteBuffer bb = buffer.toByteBuffer(position, readLen);
-
- while(bb.hasRemaining()) {
- boolean endOfInput = (cursor + readLen) >= strLen;
- CoderResult cr = decoder.decode(bb, decodeBuffer, endOfInput);
-
- if(endOfInput && cr.isUnderflow())
- cr = decoder.flush(decodeBuffer);
-
- if(cr.isOverflow()) {
- // The output CharBuffer has insufficient space
- readLen = bb.limit() - bb.remaining();
- decoder.reset();
- }
+ resetDecoder();
- if(cr.isUnderflow() && bb.hasRemaining()) {
- // input buffer doesn't have enough bytes for multi bytes characters
- if(config.getActionOnMalFormedInput() == CodingErrorAction.REPORT) {
- throw new MalformedInputException(strLen);
+ try {
+ int rawRemaining = len;
+ while (rawRemaining > 0) {
+ int bufferRemaining = buffer.size() - position;
+ if (bufferRemaining >= rawRemaining) {
+ decodeStringBuffer.append(decodeStringFastPath(rawRemaining));
+ break;
+ }
+ else if (bufferRemaining == 0) {
+ nextBuffer();
+ }
+ else {
+ ByteBuffer bb = buffer.sliceAsByteBuffer(position, bufferRemaining);
+ int bbStartPosition = bb.position();
+ decodeBuffer.clear();
+
+ CoderResult cr = decoder.decode(bb, decodeBuffer, false);
+ int readLen = bb.position() - bbStartPosition;
+ position += readLen;
+ rawRemaining -= readLen;
+ decodeStringBuffer.append(decodeBuffer.flip());
+
+ if (cr.isError()) {
+ handleCoderError(cr);
+ }
+ if (cr.isUnderflow() && readLen < bufferRemaining) {
+ // handle incomplete multibyte character
+ int incompleteMultiBytes = utf8MultibyteCharacterSize(buffer.getByte(position));
+ ByteBuffer multiByteBuffer = ByteBuffer.allocate(incompleteMultiBytes);
+ buffer.getBytes(position, buffer.size() - position, multiByteBuffer);
+
+ // read until multiByteBuffer is filled
+ while (true) {
+ nextBuffer();
+
+ int more = multiByteBuffer.remaining();
+ if (buffer.size() >= more) {
+ buffer.getBytes(0, more, multiByteBuffer);
+ position = more;
+ break;
+ }
+ else {
+ buffer.getBytes(0, buffer.size(), multiByteBuffer);
+ position = buffer.size();
}
- // trash truncated bytes
- while (bb.hasRemaining())
- bb.get();
}
-
- if(cr.isError()) {
- if((cr.isMalformed() && config.getActionOnMalFormedInput() == CodingErrorAction.REPORT) ||
- (cr.isUnmappable() && config.getActionOnUnmappableCharacter() == CodingErrorAction.REPORT)) {
+ multiByteBuffer.position(0);
+ decodeBuffer.clear();
+ cr = decoder.decode(multiByteBuffer, decodeBuffer, false);
+ if (cr.isError()) {
+ handleCoderError(cr);
+ }
+ if (cr.isOverflow() || (cr.isUnderflow() && multiByteBuffer.position() < multiByteBuffer.limit())) {
+ // isOverflow or isOverflow must not happen. if happened, throw exception
+ try {
cr.throwException();
+ throw new MessageFormatException("Unexpected UTF-8 multibyte sequence");
+ }
+ catch (Exception ex) {
+ throw new MessageFormatException("Unexpected UTF-8 multibyte sequence", ex);
}
}
-
- decodeBuffer.flip();
- sb.append(decodeBuffer);
-
- decodeBuffer.clear();
- cursor += readLen;
- consume(readLen);
+ rawRemaining -= multiByteBuffer.limit();
+ decodeStringBuffer.append(decodeBuffer.flip());
}
}
+ }
+ return decodeStringBuffer.toString();
+ }
+ catch (CharacterCodingException e) {
+ throw new MessageStringCodingException(e);
+ }
+ }
- return sb.toString();
+ private void handleCoderError(CoderResult cr)
+ throws CharacterCodingException
+ {
+ if ((cr.isMalformed() && actionOnMalformedString == CodingErrorAction.REPORT) ||
+ (cr.isUnmappable() && actionOnUnmappableString == CodingErrorAction.REPORT)) {
+ cr.throwException();
+ }
+ }
+
+ private String decodeStringFastPath(int length)
+ {
+ if (actionOnMalformedString == CodingErrorAction.REPLACE &&
+ actionOnUnmappableString == CodingErrorAction.REPLACE) {
+ String s = new String(buffer.array(), buffer.arrayOffset() + position, length, MessagePack.UTF8);
+ position += length;
+ return s;
+ }
+ else {
+ resetDecoder();
+ ByteBuffer bb = buffer.sliceAsByteBuffer();
+ bb.limit(position + length);
+ bb.position(position);
+ CharBuffer cb;
+ try {
+ cb = decoder.decode(bb);
}
- catch(CharacterCodingException e) {
+ catch (CharacterCodingException e) {
throw new MessageStringCodingException(e);
}
- } else
- return EMPTY_STRING;
+ position += length;
+ return cb.toString();
+ }
}
-
- public int unpackArrayHeader() throws IOException {
- byte b = consume();
- if(Code.isFixedArray(b)) { // fixarray
+ public int unpackArrayHeader()
+ throws IOException
+ {
+ byte b = readByte();
+ if (Code.isFixedArray(b)) { // fixarray
return b & 0x0f;
}
- switch(b) {
- case Code.ARRAY16: // array 16
- return readNextLength16();
- case Code.ARRAY32: // array 32
- return readNextLength32();
+ switch (b) {
+ case Code.ARRAY16: { // array 16
+ int len = readNextLength16();
+ return len;
+ }
+ case Code.ARRAY32: { // array 32
+ int len = readNextLength32();
+ return len;
+ }
}
throw unexpected("Array", b);
}
- public int unpackMapHeader() throws IOException {
- byte b = consume();
- if(Code.isFixedMap(b)) { // fixmap
+ public int unpackMapHeader()
+ throws IOException
+ {
+ byte b = readByte();
+ if (Code.isFixedMap(b)) { // fixmap
return b & 0x0f;
}
- switch(b) {
- case Code.MAP16: // map 16
- return readNextLength16();
- case Code.MAP32: // map 32
- return readNextLength32();
+ switch (b) {
+ case Code.MAP16: { // map 16
+ int len = readNextLength16();
+ return len;
+ }
+ case Code.MAP32: { // map 32
+ int len = readNextLength32();
+ return len;
+ }
}
throw unexpected("Map", b);
}
- public ExtendedTypeHeader unpackExtendedTypeHeader() throws IOException {
- byte b = consume();
- switch(b) {
- case Code.FIXEXT1:
- return new ExtendedTypeHeader(1, readByte());
- case Code.FIXEXT2:
- return new ExtendedTypeHeader(2, readByte());
- case Code.FIXEXT4:
- return new ExtendedTypeHeader(4, readByte());
- case Code.FIXEXT8:
- return new ExtendedTypeHeader(8, readByte());
- case Code.FIXEXT16:
- return new ExtendedTypeHeader(16, readByte());
+ public ExtensionTypeHeader unpackExtensionTypeHeader()
+ throws IOException
+ {
+ byte b = readByte();
+ switch (b) {
+ case Code.FIXEXT1: {
+ byte type = readByte();
+ return new ExtensionTypeHeader(type, 1);
+ }
+ case Code.FIXEXT2: {
+ byte type = readByte();
+ return new ExtensionTypeHeader(type, 2);
+ }
+ case Code.FIXEXT4: {
+ byte type = readByte();
+ return new ExtensionTypeHeader(type, 4);
+ }
+ case Code.FIXEXT8: {
+ byte type = readByte();
+ return new ExtensionTypeHeader(type, 8);
+ }
+ case Code.FIXEXT16: {
+ byte type = readByte();
+ return new ExtensionTypeHeader(type, 16);
+ }
case Code.EXT8: {
- int len = readNextLength8();
- int t = readByte();
- return new ExtendedTypeHeader(len, t);
+ MessageBuffer numberBuffer = prepareNumberBuffer(2);
+ int u8 = numberBuffer.getByte(nextReadPosition);
+ int length = u8 & 0xff;
+ byte type = numberBuffer.getByte(nextReadPosition + 1);
+ return new ExtensionTypeHeader(type, length);
}
case Code.EXT16: {
- int len = readNextLength16();
- int t = readByte();
- return new ExtendedTypeHeader(len, t);
+ MessageBuffer numberBuffer = prepareNumberBuffer(3);
+ int u16 = numberBuffer.getShort(nextReadPosition);
+ int length = u16 & 0xffff;
+ byte type = numberBuffer.getByte(nextReadPosition + 2);
+ return new ExtensionTypeHeader(type, length);
}
case Code.EXT32: {
- int len = readNextLength32();
- int t = readByte();
- return new ExtendedTypeHeader(len, t);
+ MessageBuffer numberBuffer = prepareNumberBuffer(5);
+ int u32 = numberBuffer.getInt(nextReadPosition);
+ if (u32 < 0) {
+ throw overflowU32Size(u32);
+ }
+ int length = u32;
+ byte type = numberBuffer.getByte(nextReadPosition + 4);
+ return new ExtensionTypeHeader(type, length);
}
}
throw unexpected("Ext", b);
}
- private int readStringHeader(byte b) throws IOException {
- switch(b) {
+ private int tryReadStringHeader(byte b)
+ throws IOException
+ {
+ switch (b) {
case Code.STR8: // str 8
return readNextLength8();
case Code.STR16: // str 16
@@ -1098,8 +1143,10 @@ private int readStringHeader(byte b) throws IOException {
}
}
- private int readBinaryHeader(byte b) throws IOException {
- switch(b) {
+ private int tryReadBinaryHeader(byte b)
+ throws IOException
+ {
+ switch (b) {
case Code.BIN8: // bin 8
return readNextLength8();
case Code.BIN16: // bin 16
@@ -1111,59 +1158,104 @@ private int readBinaryHeader(byte b) throws IOException {
}
}
-
- public int unpackRawStringHeader() throws IOException {
- byte b = consume();
- if(Code.isFixedRaw(b)) { // FixRaw
+ public int unpackRawStringHeader()
+ throws IOException
+ {
+ byte b = readByte();
+ if (Code.isFixedRaw(b)) { // FixRaw
return b & 0x1f;
}
- int len = readStringHeader(b);
- if(len >= 0)
+ int len = tryReadStringHeader(b);
+ if (len >= 0) {
return len;
+ }
- if(config.isReadBinaryAsString()){
- len = readBinaryHeader(b);
- if(len >= 0)
+ if (allowReadingBinaryAsString) {
+ len = tryReadBinaryHeader(b);
+ if (len >= 0) {
return len;
+ }
}
throw unexpected("String", b);
}
-
- public int unpackBinaryHeader() throws IOException {
- byte b = consume();
- if(Code.isFixedRaw(b)) { // FixRaw
+ public int unpackBinaryHeader()
+ throws IOException
+ {
+ byte b = readByte();
+ if (Code.isFixedRaw(b)) { // FixRaw
return b & 0x1f;
}
- int len = readBinaryHeader(b);
- if(len >= 0)
+ int len = tryReadBinaryHeader(b);
+ if (len >= 0) {
return len;
+ }
- if(config.isReadStringAsBinary()) {
- len = readStringHeader(b);
- if(len >= 0)
+ if (allowReadingStringAsBinary) {
+ len = tryReadStringHeader(b);
+ if (len >= 0) {
return len;
+ }
}
throw unexpected("Binary", b);
}
- // TODO returns a buffer reference to the payload (zero-copy)
+ /**
+ * Skip reading the specified number of bytes. Use this method only if you know skipping data is safe.
+ * For simply skipping the next value, use {@link #skipValue()}.
+ *
+ * @param numBytes
+ * @throws IOException
+ */
+ private void skipPayload(int numBytes)
+ throws IOException
+ {
+ while (true) {
+ int bufferRemaining = buffer.size() - position;
+ if (bufferRemaining >= numBytes) {
+ position += numBytes;
+ return;
+ }
+ else {
+ position += bufferRemaining;
+ numBytes -= bufferRemaining;
+ }
+ nextBuffer();
+ }
+ }
+ public void readPayload(ByteBuffer dst)
+ throws IOException
+ {
+ while (true) {
+ int dstRemaining = dst.remaining();
+ int bufferRemaining = buffer.size() - position;
+ if (bufferRemaining >= dstRemaining) {
+ buffer.getBytes(position, dstRemaining, dst);
+ position += dstRemaining;
+ return;
+ }
+ buffer.getBytes(position, bufferRemaining, dst);
+ position += bufferRemaining;
- public void readPayload(ByteBuffer dst) throws IOException {
- while(dst.remaining() > 0) {
- if(!ensureBuffer())
- throw new EOFException();
- int l = Math.min(buffer.size() - position, dst.remaining());
- buffer.getBytes(position, l, dst);
- consume(l);
+ nextBuffer();
}
}
- public void readPayload(byte[] dst) throws IOException {
+ public void readPayload(byte[] dst)
+ throws IOException
+ {
readPayload(dst, 0, dst.length);
}
+ public byte[] readPayload(int length)
+ throws IOException
+ {
+ byte[] newArray = new byte[length];
+ readPayload(newArray);
+ return newArray;
+ }
+
/**
* Read up to len bytes of data into the destination array
*
@@ -1172,91 +1264,105 @@ public void readPayload(byte[] dst) throws IOException {
* @param len the number of bytes to read
* @throws IOException
*/
- public void readPayload(byte[] dst, int off, int len) throws IOException {
- int writtenLen = 0;
- while(writtenLen < len) {
- if(!ensureBuffer())
- throw new EOFException();
- int l = Math.min(buffer.size() - position, len - writtenLen);
- buffer.getBytes(position, dst, off + writtenLen, l);
- consume(l);
- writtenLen += l;
- }
+ public void readPayload(byte[] dst, int off, int len)
+ throws IOException
+ {
+ // TODO optimize
+ readPayload(ByteBuffer.wrap(dst, off, len));
}
- public MessageBuffer readPayloadAsReference(int length) throws IOException {
- checkArgument(length >= 0);
- if(!ensure(length))
- throw new EOFException();
-
- MessageBuffer ref = buffer.slice(position, length);
- position += length;
- return ref;
+ public MessageBuffer readPayloadAsReference(int length)
+ throws IOException
+ {
+ int bufferRemaining = buffer.size() - position;
+ if (bufferRemaining >= length) {
+ MessageBuffer slice = buffer.slice(position, length);
+ position += length;
+ return slice;
+ }
+ MessageBuffer dst = MessageBuffer.allocate(length);
+ readPayload(dst.sliceAsByteBuffer());
+ return dst;
}
-
- private int readNextLength8() throws IOException {
+ private int readNextLength8()
+ throws IOException
+ {
byte u8 = readByte();
return u8 & 0xff;
}
- private int readNextLength16() throws IOException {
+ private int readNextLength16()
+ throws IOException
+ {
short u16 = readShort();
return u16 & 0xffff;
}
- private int readNextLength32() throws IOException {
+ private int readNextLength32()
+ throws IOException
+ {
int u32 = readInt();
- if(u32 < 0) {
+ if (u32 < 0) {
throw overflowU32Size(u32);
}
return u32;
}
@Override
- public void close() throws IOException {
+ public void close()
+ throws IOException
+ {
+ buffer = EMPTY_BUFFER;
+ position = 0;
in.close();
}
- private static MessageIntegerOverflowException overflowU8(byte u8) {
+ private static MessageIntegerOverflowException overflowU8(byte u8)
+ {
BigInteger bi = BigInteger.valueOf((long) (u8 & 0xff));
return new MessageIntegerOverflowException(bi);
}
- private static MessageIntegerOverflowException overflowU16(short u16) {
+ private static MessageIntegerOverflowException overflowU16(short u16)
+ {
BigInteger bi = BigInteger.valueOf((long) (u16 & 0xffff));
return new MessageIntegerOverflowException(bi);
}
- private static MessageIntegerOverflowException overflowU32(int u32) {
+ private static MessageIntegerOverflowException overflowU32(int u32)
+ {
BigInteger bi = BigInteger.valueOf((long) (u32 & 0x7fffffff) + 0x80000000L);
return new MessageIntegerOverflowException(bi);
}
- private static MessageIntegerOverflowException overflowU64(long u64) {
+ private static MessageIntegerOverflowException overflowU64(long u64)
+ {
BigInteger bi = BigInteger.valueOf(u64 + Long.MAX_VALUE + 1L).setBit(63);
return new MessageIntegerOverflowException(bi);
}
- private static MessageIntegerOverflowException overflowI16(short i16) {
+ private static MessageIntegerOverflowException overflowI16(short i16)
+ {
BigInteger bi = BigInteger.valueOf((long) i16);
return new MessageIntegerOverflowException(bi);
}
- private static MessageIntegerOverflowException overflowI32(int i32) {
+ private static MessageIntegerOverflowException overflowI32(int i32)
+ {
BigInteger bi = BigInteger.valueOf((long) i32);
return new MessageIntegerOverflowException(bi);
}
- private static MessageIntegerOverflowException overflowI64(long i64) {
+ private static MessageIntegerOverflowException overflowI64(long i64)
+ {
BigInteger bi = BigInteger.valueOf(i64);
return new MessageIntegerOverflowException(bi);
}
- private static MessageSizeException overflowU32Size(int u32) {
+ private static MessageSizeException overflowU32Size(int u32)
+ {
long lv = (long) (u32 & 0x7fffffff) + 0x80000000L;
return new MessageSizeException(lv);
}
-
-
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/NonBlockingMessageUnpacker.java b/msgpack-core/src/main/java/org/msgpack/core/NonBlockingMessageUnpacker.java
deleted file mode 100644
index 7ec3122be..000000000
--- a/msgpack-core/src/main/java/org/msgpack/core/NonBlockingMessageUnpacker.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.msgpack.core;
-
-/**
- * MessageUnpacker implementation that supports event-driven I/O, which
- * is necessary to implement an efficient RPC server that receives MessagePack data.
- */
-public class NonBlockingMessageUnpacker {
-
- // TODO Impl
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/NumberUtil.java b/msgpack-core/src/main/java/org/msgpack/core/NumberUtil.java
deleted file mode 100644
index 578833b43..000000000
--- a/msgpack-core/src/main/java/org/msgpack/core/NumberUtil.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.msgpack.core;
-
-import java.math.BigInteger;
-
-/**
- * Utilities for numbers
- */
-public class NumberUtil {
-
- private static final BigInteger BI_BYTE_MIN = BigInteger.valueOf(Byte.MIN_VALUE);
- private static final BigInteger BI_BYTE_MAX = BigInteger.valueOf(Byte.MAX_VALUE);
- private static final BigInteger BI_SHORT_MIN = BigInteger.valueOf(Short.MIN_VALUE);
- private static final BigInteger BI_SHORT_MAX = BigInteger.valueOf(Short.MAX_VALUE);
- private static final BigInteger BI_INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE);
- private static final BigInteger BI_INT_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
- private static final BigInteger BI_LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE);
- private static final BigInteger BI_LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
-
- public static class LongUtil {
-
- public static boolean isValidByte(long v) {
- return Byte.MIN_VALUE <= v && v <= Byte.MAX_VALUE;
- }
-
- public static boolean isValidByte(BigInteger v) {
- return v.compareTo(BI_BYTE_MIN) >= 0 && v.compareTo(BI_BYTE_MAX) <= 0;
- }
-
- public static boolean isValidShort(long v) {
- return Short.MIN_VALUE <= v && v <= Short.MAX_VALUE;
- }
-
- public static boolean isValidShort(BigInteger v) {
- return v.compareTo(BI_SHORT_MIN) >= 0 && v.compareTo(BI_SHORT_MAX) <= 0;
- }
-
- public static boolean isValidInt(long v) {
- return Integer.MIN_VALUE <= v && v <= Integer.MAX_VALUE;
- }
- public static boolean isValidInt(BigInteger v) {
- return v.compareTo(BI_INT_MIN) >= 0 && v.compareTo(BI_INT_MAX) <= 0;
- }
-
- public static boolean isValidLong(BigInteger v) {
- return v.compareTo(BI_LONG_MIN) >= 0 && v.compareTo(BI_LONG_MAX) <= 0;
- }
- }
-
-
-
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/Preconditions.java b/msgpack-core/src/main/java/org/msgpack/core/Preconditions.java
index b2b72f1c4..e44d97d15 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/Preconditions.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/Preconditions.java
@@ -1,3 +1,19 @@
+//
+// MessagePack for Java
+//
+// Licensed 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.
+//
+
/*
* Copyright (C) 2007 The Guava Authors
*
@@ -15,7 +31,6 @@
*/
package org.msgpack.core;
-
import org.msgpack.core.annotations.Nullable;
import org.msgpack.core.annotations.VisibleForTesting;
@@ -56,7 +71,8 @@
* @author Kevin Bourrillion
* @since 2.0 (imported from Google Collections Library)
*/
-public final class Preconditions {
+public final class Preconditions
+{
private Preconditions() {}
/**
@@ -66,7 +82,8 @@ private Preconditions() {}
* @param expression a boolean expression
* @throws IllegalArgumentException if {@code expression} is false
*/
- public static void checkArgument(boolean expression) {
+ public static void checkArgument(boolean expression)
+ {
if (!expression) {
throw new IllegalArgumentException();
}
@@ -78,11 +95,12 @@ public static void checkArgument(boolean expression) {
*
* @param expression a boolean expression
* @param errorMessage the exception message to use if the check fails; will
- * be converted to a string using {@link String#valueOf(Object)}
+ * be converted to a string using {@link String#valueOf(Object)}
* @throws IllegalArgumentException if {@code expression} is false
*/
public static void checkArgument(
- boolean expression, @Nullable Object errorMessage) {
+ boolean expression, @Nullable Object errorMessage)
+ {
if (!expression) {
throw new IllegalArgumentException(String.valueOf(errorMessage));
}
@@ -94,22 +112,23 @@ public static void checkArgument(
*
* @param expression a boolean expression
* @param errorMessageTemplate a template for the exception message should the
- * check fail. The message is formed by replacing each {@code %s}
- * placeholder in the template with an argument. These are matched by
- * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc.
- * Unmatched arguments will be appended to the formatted message in square
- * braces. Unmatched placeholders will be left as-is.
+ * check fail. The message is formed by replacing each {@code %s}
+ * placeholder in the template with an argument. These are matched by
+ * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc.
+ * Unmatched arguments will be appended to the formatted message in square
+ * braces. Unmatched placeholders will be left as-is.
* @param errorMessageArgs the arguments to be substituted into the message
- * template. Arguments are converted to strings using
- * {@link String#valueOf(Object)}.
+ * template. Arguments are converted to strings using
+ * {@link String#valueOf(Object)}.
* @throws IllegalArgumentException if {@code expression} is false
* @throws NullPointerException if the check fails and either {@code
- * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let
- * this happen)
+ * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let
+ * this happen)
*/
public static void checkArgument(boolean expression,
- @Nullable String errorMessageTemplate,
- @Nullable Object... errorMessageArgs) {
+ @Nullable String errorMessageTemplate,
+ @Nullable Object... errorMessageArgs)
+ {
if (!expression) {
throw new IllegalArgumentException(
format(errorMessageTemplate, errorMessageArgs));
@@ -123,7 +142,8 @@ public static void checkArgument(boolean expression,
* @param expression a boolean expression
* @throws IllegalStateException if {@code expression} is false
*/
- public static void checkState(boolean expression) {
+ public static void checkState(boolean expression)
+ {
if (!expression) {
throw new IllegalStateException();
}
@@ -135,11 +155,12 @@ public static void checkState(boolean expression) {
*
* @param expression a boolean expression
* @param errorMessage the exception message to use if the check fails; will
- * be converted to a string using {@link String#valueOf(Object)}
+ * be converted to a string using {@link String#valueOf(Object)}
* @throws IllegalStateException if {@code expression} is false
*/
public static void checkState(
- boolean expression, @Nullable Object errorMessage) {
+ boolean expression, @Nullable Object errorMessage)
+ {
if (!expression) {
throw new IllegalStateException(String.valueOf(errorMessage));
}
@@ -151,22 +172,23 @@ public static void checkState(
*
* @param expression a boolean expression
* @param errorMessageTemplate a template for the exception message should the
- * check fail. The message is formed by replacing each {@code %s}
- * placeholder in the template with an argument. These are matched by
- * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc.
- * Unmatched arguments will be appended to the formatted message in square
- * braces. Unmatched placeholders will be left as-is.
+ * check fail. The message is formed by replacing each {@code %s}
+ * placeholder in the template with an argument. These are matched by
+ * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc.
+ * Unmatched arguments will be appended to the formatted message in square
+ * braces. Unmatched placeholders will be left as-is.
* @param errorMessageArgs the arguments to be substituted into the message
- * template. Arguments are converted to strings using
- * {@link String#valueOf(Object)}.
+ * template. Arguments are converted to strings using
+ * {@link String#valueOf(Object)}.
* @throws IllegalStateException if {@code expression} is false
* @throws NullPointerException if the check fails and either {@code
- * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let
- * this happen)
+ * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let
+ * this happen)
*/
public static void checkState(boolean expression,
- @Nullable String errorMessageTemplate,
- @Nullable Object... errorMessageArgs) {
+ @Nullable String errorMessageTemplate,
+ @Nullable Object... errorMessageArgs)
+ {
if (!expression) {
throw new IllegalStateException(
format(errorMessageTemplate, errorMessageArgs));
@@ -181,7 +203,8 @@ public static void checkState(boolean expression,
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
*/
- public static T checkNotNull(T reference) {
+ public static T checkNotNull(T reference)
+ {
if (reference == null) {
throw new NullPointerException();
}
@@ -194,11 +217,12 @@ public static T checkNotNull(T reference) {
*
* @param reference an object reference
* @param errorMessage the exception message to use if the check fails; will
- * be converted to a string using {@link String#valueOf(Object)}
+ * be converted to a string using {@link String#valueOf(Object)}
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
*/
- public static T checkNotNull(T reference, @Nullable Object errorMessage) {
+ public static T checkNotNull(T reference, @Nullable Object errorMessage)
+ {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
}
@@ -211,20 +235,21 @@ public static T checkNotNull(T reference, @Nullable Object errorMessage) {
*
* @param reference an object reference
* @param errorMessageTemplate a template for the exception message should the
- * check fail. The message is formed by replacing each {@code %s}
- * placeholder in the template with an argument. These are matched by
- * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc.
- * Unmatched arguments will be appended to the formatted message in square
- * braces. Unmatched placeholders will be left as-is.
+ * check fail. The message is formed by replacing each {@code %s}
+ * placeholder in the template with an argument. These are matched by
+ * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc.
+ * Unmatched arguments will be appended to the formatted message in square
+ * braces. Unmatched placeholders will be left as-is.
* @param errorMessageArgs the arguments to be substituted into the message
- * template. Arguments are converted to strings using
- * {@link String#valueOf(Object)}.
+ * template. Arguments are converted to strings using
+ * {@link String#valueOf(Object)}.
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
*/
public static T checkNotNull(T reference,
- @Nullable String errorMessageTemplate,
- @Nullable Object... errorMessageArgs) {
+ @Nullable String errorMessageTemplate,
+ @Nullable Object... errorMessageArgs)
+ {
if (reference == null) {
// If either of these parameters is null, the right thing happens anyway
throw new NullPointerException(
@@ -268,14 +293,15 @@ public static T checkNotNull(T reference,
* inclusive, to {@code size}, exclusive.
*
* @param index a user-supplied index identifying an element of an array, list
- * or string
+ * or string
* @param size the size of that array, list or string
* @return the value of {@code index}
* @throws IndexOutOfBoundsException if {@code index} is negative or is not
- * less than {@code size}
+ * less than {@code size}
* @throws IllegalArgumentException if {@code size} is negative
*/
- public static int checkElementIndex(int index, int size) {
+ public static int checkElementIndex(int index, int size)
+ {
return checkElementIndex(index, size, "index");
}
@@ -285,16 +311,17 @@ public static int checkElementIndex(int index, int size) {
* inclusive, to {@code size}, exclusive.
*
* @param index a user-supplied index identifying an element of an array, list
- * or string
+ * or string
* @param size the size of that array, list or string
* @param desc the text to use to describe this index in an error message
* @return the value of {@code index}
* @throws IndexOutOfBoundsException if {@code index} is negative or is not
- * less than {@code size}
+ * less than {@code size}
* @throws IllegalArgumentException if {@code size} is negative
*/
public static int checkElementIndex(
- int index, int size, @Nullable String desc) {
+ int index, int size, @Nullable String desc)
+ {
// Carefully optimized for execution by hotspot (explanatory comment above)
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException(badElementIndex(index, size, desc));
@@ -302,12 +329,15 @@ public static int checkElementIndex(
return index;
}
- private static String badElementIndex(int index, int size, String desc) {
+ private static String badElementIndex(int index, int size, String desc)
+ {
if (index < 0) {
return format("%s (%s) must not be negative", desc, index);
- } else if (size < 0) {
+ }
+ else if (size < 0) {
throw new IllegalArgumentException("negative size: " + size);
- } else { // index >= size
+ }
+ else { // index >= size
return format("%s (%s) must be less than size (%s)", desc, index, size);
}
}
@@ -318,14 +348,15 @@ private static String badElementIndex(int index, int size, String desc) {
* to {@code size}, inclusive.
*
* @param index a user-supplied index identifying a position in an array, list
- * or string
+ * or string
* @param size the size of that array, list or string
* @return the value of {@code index}
* @throws IndexOutOfBoundsException if {@code index} is negative or is
- * greater than {@code size}
+ * greater than {@code size}
* @throws IllegalArgumentException if {@code size} is negative
*/
- public static int checkPositionIndex(int index, int size) {
+ public static int checkPositionIndex(int index, int size)
+ {
return checkPositionIndex(index, size, "index");
}
@@ -335,16 +366,17 @@ public static int checkPositionIndex(int index, int size) {
* to {@code size}, inclusive.
*
* @param index a user-supplied index identifying a position in an array, list
- * or string
+ * or string
* @param size the size of that array, list or string
* @param desc the text to use to describe this index in an error message
* @return the value of {@code index}
* @throws IndexOutOfBoundsException if {@code index} is negative or is
- * greater than {@code size}
+ * greater than {@code size}
* @throws IllegalArgumentException if {@code size} is negative
*/
public static int checkPositionIndex(
- int index, int size, @Nullable String desc) {
+ int index, int size, @Nullable String desc)
+ {
// Carefully optimized for execution by hotspot (explanatory comment above)
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc));
@@ -352,12 +384,15 @@ public static int checkPositionIndex(
return index;
}
- private static String badPositionIndex(int index, int size, String desc) {
+ private static String badPositionIndex(int index, int size, String desc)
+ {
if (index < 0) {
return format("%s (%s) must not be negative", desc, index);
- } else if (size < 0) {
+ }
+ else if (size < 0) {
throw new IllegalArgumentException("negative size: " + size);
- } else { // index > size
+ }
+ else { // index > size
return format("%s (%s) must not be greater than size (%s)",
desc, index, size);
}
@@ -369,22 +404,24 @@ private static String badPositionIndex(int index, int size, String desc) {
* position index may range from zero to {@code size}, inclusive.
*
* @param start a user-supplied index identifying a starting position in an
- * array, list or string
+ * array, list or string
* @param end a user-supplied index identifying a ending position in an array,
- * list or string
+ * list or string
* @param size the size of that array, list or string
* @throws IndexOutOfBoundsException if either index is negative or is
- * greater than {@code size}, or if {@code end} is less than {@code start}
+ * greater than {@code size}, or if {@code end} is less than {@code start}
* @throws IllegalArgumentException if {@code size} is negative
*/
- public static void checkPositionIndexes(int start, int end, int size) {
+ public static void checkPositionIndexes(int start, int end, int size)
+ {
// Carefully optimized for execution by hotspot (explanatory comment above)
if (start < 0 || end < start || end > size) {
throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size));
}
}
- private static String badPositionIndexes(int start, int end, int size) {
+ private static String badPositionIndexes(int start, int end, int size)
+ {
if (start < 0 || start > size) {
return badPositionIndex(start, size, "start index");
}
@@ -403,14 +440,15 @@ private static String badPositionIndexes(int start, int end, int size) {
* be appended to the end of the formatted message in square braces.
*
* @param template a non-null string containing 0 or more {@code %s}
- * placeholders.
+ * placeholders.
* @param args the arguments to be substituted into the message
- * template. Arguments are converted to strings using
- * {@link String#valueOf(Object)}. Arguments can be null.
+ * template. Arguments are converted to strings using
+ * {@link String#valueOf(Object)}. Arguments can be null.
*/
@VisibleForTesting
static String format(String template,
- @Nullable Object... args) {
+ @Nullable Object... args)
+ {
template = String.valueOf(template); // null -> "null"
// start substituting the arguments into the '%s' placeholders
@@ -442,4 +480,4 @@ static String format(String template,
return builder.toString();
}
-}
\ No newline at end of file
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/annotations/Insecure.java b/msgpack-core/src/main/java/org/msgpack/core/annotations/Insecure.java
deleted file mode 100644
index 11d0fade3..000000000
--- a/msgpack-core/src/main/java/org/msgpack/core/annotations/Insecure.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.msgpack.core.annotations;
-
-/**
- * Annotates a code which must be used carefully.
- */
-public @interface Insecure {
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/annotations/Nullable.java b/msgpack-core/src/main/java/org/msgpack/core/annotations/Nullable.java
index 0871b6ec1..9e7c94fab 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/annotations/Nullable.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/annotations/Nullable.java
@@ -1,7 +1,23 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.annotations;
/**
* Annotates a field which can be null
*/
-public @interface Nullable {
+public @interface Nullable
+{
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/annotations/VisibleForTesting.java b/msgpack-core/src/main/java/org/msgpack/core/annotations/VisibleForTesting.java
index 3c9876851..ddd4aa4c9 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/annotations/VisibleForTesting.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/annotations/VisibleForTesting.java
@@ -1,7 +1,23 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.annotations;
/**
* Annotates a code which is used only for testing.
*/
-public @interface VisibleForTesting {
+public @interface VisibleForTesting
+{
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/ArrayBufferInput.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/ArrayBufferInput.java
index ba7889698..36f0ad3c1 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/buffer/ArrayBufferInput.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/buffer/ArrayBufferInput.java
@@ -1,52 +1,87 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer;
import java.io.IOException;
-import static org.msgpack.core.Preconditions.*;
+
+import static org.msgpack.core.Preconditions.checkNotNull;
/**
* MessageBufferInput adapter for byte arrays
*/
-public class ArrayBufferInput implements MessageBufferInput {
-
+public class ArrayBufferInput
+ implements MessageBufferInput
+{
private MessageBuffer buffer;
private boolean isRead = false;
- public ArrayBufferInput(MessageBuffer buf) {
+ public ArrayBufferInput(MessageBuffer buf)
+ {
this.buffer = checkNotNull(buf, "input buffer is null");
}
- public ArrayBufferInput(byte[] arr) {
+ public ArrayBufferInput(byte[] arr)
+ {
this(arr, 0, arr.length);
}
- public ArrayBufferInput(byte[] arr, int offset, int length) {
- checkArgument(offset + length <= arr.length);
- this.buffer = MessageBuffer.wrap(checkNotNull(arr, "input array is null")).slice(offset, length);
+ public ArrayBufferInput(byte[] arr, int offset, int length)
+ {
+ this(MessageBuffer.wrap(checkNotNull(arr, "input array is null")).slice(offset, length));
}
- public void reset(MessageBuffer buf) {
+ /**
+ * Reset buffer. This method doesn't close the old resource.
+ *
+ * @param buf new buffer
+ * @return the old resource
+ */
+ public MessageBuffer reset(MessageBuffer buf)
+ {
+ MessageBuffer old = this.buffer;
this.buffer = buf;
this.isRead = false;
+ return old;
}
- public void reset(byte[] arr) {
+ public void reset(byte[] arr)
+ {
reset(MessageBuffer.wrap(checkNotNull(arr, "input array is null")));
}
- public void reset(byte[] arr, int offset, int len) {
+ public void reset(byte[] arr, int offset, int len)
+ {
reset(MessageBuffer.wrap(checkNotNull(arr, "input array is null")).slice(offset, len));
}
@Override
- public MessageBuffer next() throws IOException {
- if(isRead)
+ public MessageBuffer next()
+ throws IOException
+ {
+ if (isRead) {
return null;
+ }
isRead = true;
return buffer;
}
@Override
- public void close() throws IOException {
+ public void close()
+ throws IOException
+ {
buffer = null;
isRead = false;
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/ArrayBufferOutput.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/ArrayBufferOutput.java
new file mode 100644
index 000000000..186a579af
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/core/buffer/ArrayBufferOutput.java
@@ -0,0 +1,136 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * MessageBufferOutput adapter that packs data into list of byte arrays.
+ */
+public class ArrayBufferOutput
+ implements MessageBufferOutput
+{
+ private List list;
+ private MessageBuffer lastBuffer;
+ private int bufferSize;
+
+ public ArrayBufferOutput()
+ {
+ this(8192);
+ }
+
+ public ArrayBufferOutput(int bufferSize)
+ {
+ this.bufferSize = bufferSize;
+ this.list = new ArrayList();
+ }
+
+ public int getSize()
+ {
+ int size = 0;
+ for (MessageBuffer buffer : list) {
+ size += buffer.size();
+ }
+ return size;
+ }
+
+ public byte[] toByteArray()
+ {
+ byte[] data = new byte[getSize()];
+ int off = 0;
+ for (MessageBuffer buffer : list) {
+ buffer.getBytes(0, data, off, buffer.size());
+ off += buffer.size();
+ }
+ return data;
+ }
+
+ public MessageBuffer toMessageBuffer()
+ {
+ if (list.size() == 1) {
+ return list.get(0);
+ }
+ else if (list.isEmpty()) {
+ return MessageBuffer.allocate(0);
+ }
+ else {
+ return MessageBuffer.wrap(toByteArray());
+ }
+ }
+
+ public List toBufferList()
+ {
+ return new ArrayList(list);
+ }
+
+ /**
+ * Clears the internal buffers
+ */
+ public void clear()
+ {
+ list.clear();
+ }
+
+ @Override
+ public MessageBuffer next(int mimimumSize)
+ {
+ if (lastBuffer != null && lastBuffer.size() > mimimumSize) {
+ return lastBuffer;
+ }
+ else {
+ int size = Math.max(bufferSize, mimimumSize);
+ MessageBuffer buffer = MessageBuffer.allocate(size);
+ lastBuffer = buffer;
+ return buffer;
+ }
+ }
+
+ @Override
+ public void writeBuffer(int length)
+ {
+ list.add(lastBuffer.slice(0, length));
+ if (lastBuffer.size() - length > bufferSize / 4) {
+ lastBuffer = lastBuffer.slice(length, lastBuffer.size() - length);
+ }
+ else {
+ lastBuffer = null;
+ }
+ }
+
+ @Override
+ public void write(byte[] buffer, int offset, int length)
+ {
+ MessageBuffer copy = MessageBuffer.allocate(length);
+ copy.putBytes(0, buffer, offset, length);
+ list.add(copy);
+ }
+
+ @Override
+ public void add(byte[] buffer, int offset, int length)
+ {
+ MessageBuffer wrapped = MessageBuffer.wrap(buffer, offset, length);
+ list.add(wrapped);
+ }
+
+ @Override
+ public void close()
+ { }
+
+ @Override
+ public void flush()
+ { }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/ByteBufferInput.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/ByteBufferInput.java
deleted file mode 100644
index f3f1f3244..000000000
--- a/msgpack-core/src/main/java/org/msgpack/core/buffer/ByteBufferInput.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.msgpack.core.buffer;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import static org.msgpack.core.Preconditions.*;
-
-/**
- * {@link MessageBufferInput} adapter for {@link java.nio.ByteBuffer}
- */
-public class ByteBufferInput implements MessageBufferInput {
-
- private ByteBuffer input;
- private boolean isRead = false;
-
- public ByteBufferInput(ByteBuffer input) {
- this.input = checkNotNull(input, "input ByteBuffer is null");
- }
-
- public void reset(ByteBuffer input) {
- this.input = input;
- isRead = false;
- }
-
- @Override
- public MessageBuffer next() throws IOException {
- if(isRead)
- return null;
-
- isRead = true;
- return MessageBuffer.wrap(input);
- }
-
-
- @Override
- public void close() throws IOException {
- // Nothing to do
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/ChannelBufferInput.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/ChannelBufferInput.java
index f4c85305b..f00cb0c30 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/buffer/ChannelBufferInput.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/buffer/ChannelBufferInput.java
@@ -1,56 +1,81 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
-import static org.msgpack.core.Preconditions.*;
+import static org.msgpack.core.Preconditions.checkArgument;
+import static org.msgpack.core.Preconditions.checkNotNull;
/**
* {@link MessageBufferInput} adapter for {@link java.nio.channels.ReadableByteChannel}
*/
-public class ChannelBufferInput implements MessageBufferInput {
-
+public class ChannelBufferInput
+ implements MessageBufferInput
+{
private ReadableByteChannel channel;
- private boolean reachedEOF = false;
- private final int bufferSize;
+ private final MessageBuffer buffer;
- public ChannelBufferInput(ReadableByteChannel channel) {
+ public ChannelBufferInput(ReadableByteChannel channel)
+ {
this(channel, 8192);
}
- public ChannelBufferInput(ReadableByteChannel channel, int bufferSize) {
+ public ChannelBufferInput(ReadableByteChannel channel, int bufferSize)
+ {
this.channel = checkNotNull(channel, "input channel is null");
checkArgument(bufferSize > 0, "buffer size must be > 0: " + bufferSize);
- this.bufferSize = bufferSize;
+ this.buffer = MessageBuffer.allocate(bufferSize);
}
- public void reset(ReadableByteChannel channel) throws IOException {
- channel.close();
+ /**
+ * Reset channel. This method doesn't close the old resource.
+ *
+ * @param channel new channel
+ * @return the old resource
+ */
+ public ReadableByteChannel reset(ReadableByteChannel channel)
+ throws IOException
+ {
+ ReadableByteChannel old = this.channel;
this.channel = channel;
- this.reachedEOF = false;
+ return old;
}
- @Override
- public MessageBuffer next() throws IOException {
- if(reachedEOF) {
- return null;
- }
-
- MessageBuffer m = MessageBuffer.newBuffer(bufferSize);
- ByteBuffer b = m.toByteBuffer();
- while(!reachedEOF && b.remaining() > 0) {
+ @Override
+ public MessageBuffer next()
+ throws IOException
+ {
+ ByteBuffer b = buffer.sliceAsByteBuffer();
+ while (b.remaining() > 0) {
int ret = channel.read(b);
- if(ret == -1) {
- reachedEOF = true;
+ if (ret == -1) {
+ break;
}
}
b.flip();
- return b.remaining() == 0 ? null : m.slice(0, b.limit());
+ return b.remaining() == 0 ? null : buffer.slice(0, b.limit());
}
@Override
- public void close() throws IOException {
+ public void close()
+ throws IOException
+ {
channel.close();
}
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/ChannelBufferOutput.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/ChannelBufferOutput.java
index a0e098819..32969f29a 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/buffer/ChannelBufferOutput.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/buffer/ChannelBufferOutput.java
@@ -1,3 +1,18 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer;
import java.io.IOException;
@@ -9,37 +24,83 @@
/**
* {@link MessageBufferOutput} adapter for {@link java.nio.channels.WritableByteChannel}
*/
-public class ChannelBufferOutput implements MessageBufferOutput {
-
+public class ChannelBufferOutput
+ implements MessageBufferOutput
+{
private WritableByteChannel channel;
private MessageBuffer buffer;
- public ChannelBufferOutput(WritableByteChannel channel) {
+ public ChannelBufferOutput(WritableByteChannel channel)
+ {
+ this(channel, 8192);
+ }
+
+ public ChannelBufferOutput(WritableByteChannel channel, int bufferSize)
+ {
this.channel = checkNotNull(channel, "output channel is null");
+ this.buffer = MessageBuffer.allocate(bufferSize);
}
- public void reset(WritableByteChannel channel) throws IOException {
- channel.close();
+ /**
+ * Reset channel. This method doesn't close the old channel.
+ *
+ * @param channel new channel
+ * @return the old channel
+ */
+ public WritableByteChannel reset(WritableByteChannel channel)
+ throws IOException
+ {
+ WritableByteChannel old = this.channel;
this.channel = channel;
+ return old;
}
-
@Override
- public MessageBuffer next(int bufferSize) throws IOException {
- if(buffer == null || buffer.size() != bufferSize) {
- buffer = MessageBuffer.newBuffer(bufferSize);
+ public MessageBuffer next(int mimimumSize)
+ throws IOException
+ {
+ if (buffer.size() < mimimumSize) {
+ buffer = MessageBuffer.allocate(mimimumSize);
}
return buffer;
}
@Override
- public void flush(MessageBuffer buf) throws IOException {
- ByteBuffer bb = buf.toByteBuffer();
- channel.write(bb);
+ public void writeBuffer(int length)
+ throws IOException
+ {
+ ByteBuffer bb = buffer.sliceAsByteBuffer(0, length);
+ while (bb.hasRemaining()) {
+ channel.write(bb);
+ }
}
@Override
- public void close() throws IOException {
+ public void write(byte[] buffer, int offset, int length)
+ throws IOException
+ {
+ ByteBuffer bb = ByteBuffer.wrap(buffer, offset, length);
+ while (bb.hasRemaining()) {
+ channel.write(bb);
+ }
+ }
+
+ @Override
+ public void add(byte[] buffer, int offset, int length)
+ throws IOException
+ {
+ write(buffer, offset, length);
+ }
+
+ @Override
+ public void close()
+ throws IOException
+ {
channel.close();
}
+
+ @Override
+ public void flush()
+ throws IOException
+ { }
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/DirectBufferAccess.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/DirectBufferAccess.java
new file mode 100644
index 000000000..ab86061d3
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/core/buffer/DirectBufferAccess.java
@@ -0,0 +1,158 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+
+/**
+ * Wraps the difference of access methods to DirectBuffers between Android and others.
+ */
+class DirectBufferAccess
+{
+ private DirectBufferAccess()
+ {}
+
+ enum DirectBufferConstructorType
+ {
+ ARGS_LONG_INT_REF,
+ ARGS_LONG_INT,
+ ARGS_INT_INT,
+ ARGS_MB_INT_INT
+ }
+
+ static Method mGetAddress;
+ static Method mCleaner;
+ static Method mClean;
+
+ // TODO We should use MethodHandle for efficiency, but it is not available in JDK6
+ static Constructor byteBufferConstructor;
+ static Class> directByteBufferClass;
+ static DirectBufferConstructorType directBufferConstructorType;
+ static Method memoryBlockWrapFromJni;
+
+ static {
+ try {
+ // Find the hidden constructor for DirectByteBuffer
+ directByteBufferClass = ClassLoader.getSystemClassLoader().loadClass("java.nio.DirectByteBuffer");
+ Constructor directByteBufferConstructor = null;
+ DirectBufferConstructorType constructorType = null;
+ Method mbWrap = null;
+ try {
+ // TODO We should use MethodHandle for Java7, which can avoid the cost of boxing with JIT optimization
+ directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(long.class, int.class, Object.class);
+ constructorType = DirectBufferConstructorType.ARGS_LONG_INT_REF;
+ }
+ catch (NoSuchMethodException e0) {
+ try {
+ // https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/java/nio/DirectByteBuffer.java
+ // DirectByteBuffer(long address, int capacity)
+ directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(long.class, int.class);
+ constructorType = DirectBufferConstructorType.ARGS_LONG_INT;
+ }
+ catch (NoSuchMethodException e1) {
+ try {
+ directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(int.class, int.class);
+ constructorType = DirectBufferConstructorType.ARGS_INT_INT;
+ }
+ catch (NoSuchMethodException e2) {
+ Class> aClass = Class.forName("java.nio.MemoryBlock");
+ mbWrap = aClass.getDeclaredMethod("wrapFromJni", int.class, long.class);
+ mbWrap.setAccessible(true);
+ directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(aClass, int.class, int.class);
+ constructorType = DirectBufferConstructorType.ARGS_MB_INT_INT;
+ }
+ }
+ }
+
+ byteBufferConstructor = directByteBufferConstructor;
+ directBufferConstructorType = constructorType;
+ memoryBlockWrapFromJni = mbWrap;
+
+ if (byteBufferConstructor == null) {
+ throw new RuntimeException("Constructor of DirectByteBuffer is not found");
+ }
+ byteBufferConstructor.setAccessible(true);
+
+ mGetAddress = directByteBufferClass.getDeclaredMethod("address");
+ mGetAddress.setAccessible(true);
+
+ mCleaner = directByteBufferClass.getDeclaredMethod("cleaner");
+ mCleaner.setAccessible(true);
+
+ mClean = mCleaner.getReturnType().getDeclaredMethod("clean");
+ mClean.setAccessible(true);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static long getAddress(Object base)
+ {
+ try {
+ return (Long) mGetAddress.invoke(base);
+ }
+ catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static void clean(Object base)
+ {
+ try {
+ Object cleaner = mCleaner.invoke(base);
+ mClean.invoke(cleaner);
+ }
+ catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static boolean isDirectByteBufferInstance(Object s)
+ {
+ return directByteBufferClass.isInstance(s);
+ }
+
+ static ByteBuffer newByteBuffer(long address, int index, int length, ByteBuffer reference)
+ {
+ try {
+ switch (directBufferConstructorType) {
+ case ARGS_LONG_INT_REF:
+ return (ByteBuffer) byteBufferConstructor.newInstance(address + index, length, reference);
+ case ARGS_LONG_INT:
+ return (ByteBuffer) byteBufferConstructor.newInstance(address + index, length);
+ case ARGS_INT_INT:
+ return (ByteBuffer) byteBufferConstructor.newInstance((int) address + index, length);
+ case ARGS_MB_INT_INT:
+ return (ByteBuffer) byteBufferConstructor.newInstance(
+ memoryBlockWrapFromJni.invoke(null, address + index, length),
+ length, 0);
+ default:
+ throw new IllegalStateException("Unexpected value");
+ }
+ }
+ catch (Throwable e) {
+ // Convert checked exception to unchecked exception
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/InputStreamBufferInput.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/InputStreamBufferInput.java
index 08f437c42..d605fec3a 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/buffer/InputStreamBufferInput.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/buffer/InputStreamBufferInput.java
@@ -1,75 +1,88 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer;
-import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.lang.reflect.Field;
+import java.nio.channels.FileChannel;
import static org.msgpack.core.Preconditions.checkNotNull;
/**
* {@link MessageBufferInput} adapter for {@link InputStream}
*/
-public class InputStreamBufferInput implements MessageBufferInput {
-
+public class InputStreamBufferInput
+ implements MessageBufferInput
+{
private InputStream in;
- private final int bufferSize;
- private boolean reachedEOF = false;
+ private final byte[] buffer;
- public static MessageBufferInput newBufferInput(InputStream in) {
+ public static MessageBufferInput newBufferInput(InputStream in)
+ {
checkNotNull(in, "InputStream is null");
if (in instanceof FileInputStream) {
- return new ChannelBufferInput(((FileInputStream) in).getChannel());
+ FileChannel channel = ((FileInputStream) in).getChannel();
+ if (channel != null) {
+ return new ChannelBufferInput(channel);
+ }
}
return new InputStreamBufferInput(in);
}
- public InputStreamBufferInput(InputStream in) {
+ public InputStreamBufferInput(InputStream in)
+ {
this(in, 8192);
}
- public InputStreamBufferInput(InputStream in, int bufferSize) {
+ public InputStreamBufferInput(InputStream in, int bufferSize)
+ {
this.in = checkNotNull(in, "input is null");
- this.bufferSize = bufferSize;
+ this.buffer = new byte[bufferSize];
}
- public void reset(InputStream in) throws IOException {
- in.close();
+ /**
+ * Reset Stream. This method doesn't close the old resource.
+ *
+ * @param in new stream
+ * @return the old resource
+ */
+ public InputStream reset(InputStream in)
+ throws IOException
+ {
+ InputStream old = this.in;
this.in = in;
- reachedEOF = false;
+ return old;
}
-
@Override
- public MessageBuffer next() throws IOException {
- if(reachedEOF)
+ public MessageBuffer next()
+ throws IOException
+ {
+ int readLen = in.read(buffer);
+ if (readLen == -1) {
return null;
-
- byte[] buffer = null;
- int cursor = 0;
- while(!reachedEOF && cursor < bufferSize) {
- if(buffer == null)
- buffer = new byte[bufferSize];
-
- int readLen = in.read(buffer, cursor, bufferSize - cursor);
- if(readLen == -1) {
- reachedEOF = true;
- break;
- }
- cursor += readLen;
}
-
- return buffer == null ? null : MessageBuffer.wrap(buffer).slice(0, cursor);
+ return MessageBuffer.wrap(buffer, 0, readLen);
}
@Override
- public void close() throws IOException {
- try {
- in.close();
- }
- finally {
-
- }
+ public void close()
+ throws IOException
+ {
+ in.close();
}
}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBuffer.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBuffer.java
index cec8d01e3..d4d5f2238 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBuffer.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBuffer.java
@@ -1,132 +1,157 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer;
-import org.msgpack.core.annotations.Insecure;
import sun.misc.Unsafe;
-import sun.nio.ch.DirectBuffer;
-//import java.lang.invoke.MethodHandle;
-//import java.lang.invoke.MethodHandles;
-//import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
-import static org.msgpack.core.Preconditions.*;
+import java.nio.ByteOrder;
+
+import static org.msgpack.core.Preconditions.checkArgument;
+import static org.msgpack.core.Preconditions.checkNotNull;
/**
* MessageBuffer class is an abstraction of memory for reading/writing message packed data.
* This MessageBuffers ensures short/int/float/long/double values are written in the big-endian order.
- *
+ *
* This class is optimized for fast memory access, so many methods are
* implemented without using any interface method that produces invokeinterface call in JVM.
* Compared to invokevirtual, invokeinterface is 30% slower in general because it needs to find a target function from the table.
- *
*/
-public class MessageBuffer {
-
+public class MessageBuffer
+{
+ static final boolean isUniversalBuffer;
static final Unsafe unsafe;
- // TODO We should use MethodHandle for efficiency, but it is not available in JDK6
- static final Constructor byteBufferConstructor;
- static final boolean isByteBufferConstructorTakesBufferReference;
+
+ /**
+ * Reference to MessageBuffer Constructors
+ */
+ private static final Constructor> mbArrConstructor;
+
+ /**
+ * The offset from the object memory header to its byte array data
+ */
static final int ARRAY_BYTE_BASE_OFFSET;
- static final int ARRAY_BYTE_INDEX_SCALE;
- static {
- try {
- // Fetch theUnsafe object for Orackle JDK and OpenJDK
- Unsafe u;
- try {
- Field field = Unsafe.class.getDeclaredField("theUnsafe");
- field.setAccessible(true);
- u = (Unsafe) field.get(null);
- }
- catch(NoSuchFieldException e) {
- // Workaround for creating an Unsafe instance for Android OS
- Constructor unsafeConstructor = Unsafe.class.getDeclaredConstructor();
- unsafeConstructor.setAccessible(true);
- u = (Unsafe) unsafeConstructor.newInstance();
- }
- unsafe = u;
- if (unsafe == null) {
- throw new RuntimeException("Unsafe is unavailable");
- }
+ private static final String UNIVERSAL_MESSAGE_BUFFER = "org.msgpack.core.buffer.MessageBufferU";
+ private static final String BIGENDIAN_MESSAGE_BUFFER = "org.msgpack.core.buffer.MessageBufferBE";
+ private static final String DEFAULT_MESSAGE_BUFFER = "org.msgpack.core.buffer.MessageBuffer";
- ARRAY_BYTE_BASE_OFFSET = unsafe.arrayBaseOffset(byte[].class);
- ARRAY_BYTE_INDEX_SCALE = unsafe.arrayIndexScale(byte[].class);
+ static {
+ boolean useUniversalBuffer = false;
+ Unsafe unsafeInstance = null;
+ int arrayByteBaseOffset = 16;
- // Make sure the VM thinks bytes are only one byte wide
- if (ARRAY_BYTE_INDEX_SCALE != 1) {
- throw new IllegalStateException("Byte array index scale must be 1, but is " + ARRAY_BYTE_INDEX_SCALE);
+ try {
+ // Check java version
+ String javaVersion = System.getProperty("java.specification.version", "");
+ int dotPos = javaVersion.indexOf('.');
+ boolean isJavaAtLeast7 = false;
+ if (dotPos != -1) {
+ try {
+ int major = Integer.parseInt(javaVersion.substring(0, dotPos));
+ int minor = Integer.parseInt(javaVersion.substring(dotPos + 1));
+ isJavaAtLeast7 = major > 1 || (major == 1 && minor >= 7);
+ }
+ catch (NumberFormatException e) {
+ e.printStackTrace(System.err);
+ }
}
- // Find the hidden constructor for DirectByteBuffer
- Class> directByteBufferClass = ClassLoader.getSystemClassLoader().loadClass("java.nio.DirectByteBuffer");
- Constructor directByteBufferConstructor = null;
- boolean isAcceptReference = true;
+ boolean hasUnsafe = false;
try {
- // TODO We should use MethodHandle for Java7, which can avoid the cost of boxing with JIT optimization
- directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(long.class, int.class, Object.class);
- }
- catch(NoSuchMethodException e) {
- directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(long.class, int.class);
- isAcceptReference = true;
+ hasUnsafe = Class.forName("sun.misc.Unsafe") != null;
}
- byteBufferConstructor = directByteBufferConstructor;
- isByteBufferConstructorTakesBufferReference = isAcceptReference;
- if(byteBufferConstructor == null)
- throw new RuntimeException("Constructor of DirectByteBuffer is not found");
- byteBufferConstructor.setAccessible(true);
-
- // Check the endian of this CPU
- boolean isLittleEndian = true;
- byte[] a = new byte[8];
- unsafe.putLong(a, (long) ARRAY_BYTE_BASE_OFFSET, 0x0102030405060708L);
- switch (a[0]) {
- case 0x01:
- isLittleEndian = false;
- break;
- case 0x08:
- isLittleEndian = true;
- break;
- default:
- assert false;
+ catch (Exception e) {
}
- String bufferClsName = isLittleEndian ? "org.msgpack.core.buffer.MessageBuffer" : "org.msgpack.core.buffer.MessageBufferBE";
- Class> bufferCls = Class.forName(bufferClsName);
- msgBufferClass = bufferCls;
+ // Detect android VM
+ boolean isAndroid = System.getProperty("java.runtime.name", "").toLowerCase().contains("android");
+
+ // Is Google App Engine?
+ boolean isGAE = System.getProperty("com.google.appengine.runtime.version") != null;
- Constructor> mbArrCstr = bufferCls.getDeclaredConstructor(byte[].class);
- mbArrCstr.setAccessible(true);
- mbArrConstructor = mbArrCstr;
+ // For Java6, android and JVM that has no Unsafe class, use Universal MessageBuffer
+ useUniversalBuffer =
+ Boolean.parseBoolean(System.getProperty("msgpack.universal-buffer", "false"))
+ || isAndroid
+ || isGAE
+ || !isJavaAtLeast7
+ || !hasUnsafe;
- Constructor> mbBBCstr = bufferCls.getDeclaredConstructor(ByteBuffer.class);
- mbBBCstr.setAccessible(true);
- mbBBConstructor = mbBBCstr;
+ if (!useUniversalBuffer) {
+ // Fetch theUnsafe object for Oracle and OpenJDK
+ Field field = Unsafe.class.getDeclaredField("theUnsafe");
+ field.setAccessible(true);
+ unsafeInstance = (Unsafe) field.get(null);
+ if (unsafeInstance == null) {
+ throw new RuntimeException("Unsafe is unavailable");
+ }
+ arrayByteBaseOffset = unsafeInstance.arrayBaseOffset(byte[].class);
+ int arrayByteIndexScale = unsafeInstance.arrayIndexScale(byte[].class);
- // Requires Java7
- //newMsgBuffer = MethodHandles.lookup().unreflectConstructor(mbArrCstr).asType(
- // MethodType.methodType(bufferCls, byte[].class)
- //);
+ // Make sure the VM thinks bytes are only one byte wide
+ if (arrayByteIndexScale != 1) {
+ throw new IllegalStateException("Byte array index scale must be 1, but is " + arrayByteIndexScale);
+ }
+ }
}
catch (Exception e) {
e.printStackTrace(System.err);
- throw new RuntimeException(e);
+ // Use MessageBufferU
+ useUniversalBuffer = true;
}
- }
-
-
- /**
- * MessageBuffer class to use. If this machine is big-endian, it uses MessageBufferBE, which overrides some methods in this class that translate endians. If not, uses MessageBuffer.
- */
- private final static Class> msgBufferClass;
+ finally {
+ // Initialize the static fields
+ unsafe = unsafeInstance;
+ ARRAY_BYTE_BASE_OFFSET = arrayByteBaseOffset;
+
+ // Switch MessageBuffer implementation according to the environment
+ isUniversalBuffer = useUniversalBuffer;
+ String bufferClsName;
+ if (isUniversalBuffer) {
+ bufferClsName = UNIVERSAL_MESSAGE_BUFFER;
+ }
+ else {
+ // Check the endian of this CPU
+ boolean isLittleEndian = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
+ bufferClsName = isLittleEndian ? DEFAULT_MESSAGE_BUFFER : BIGENDIAN_MESSAGE_BUFFER;
+ }
- private final static Constructor> mbArrConstructor;
- private final static Constructor> mbBBConstructor;
+ try {
+ // We need to use reflection here to find MessageBuffer implementation classes because
+ // importing these classes creates TypeProfile and adds some overhead to method calls.
+ // MessageBufferX (default, BE or U) class
+ Class> bufferCls = Class.forName(bufferClsName);
- // Requires Java7
- //private final static MethodHandle newMsgBuffer;
+ // MessageBufferX(byte[]) constructor
+ Constructor> mbArrCstr = bufferCls.getDeclaredConstructor(byte[].class, int.class, int.class);
+ mbArrCstr.setAccessible(true);
+ mbArrConstructor = mbArrCstr;
+ }
+ catch (Exception e) {
+ e.printStackTrace(System.err);
+ throw new RuntimeException(e); // No more fallback exists if MessageBuffer constructors are inaccessible
+ }
+ }
+ }
/**
* Base object for resolving the relative address of the raw byte array.
@@ -145,360 +170,295 @@ public class MessageBuffer {
*/
protected final int size;
- /**
- * Reference is used to hold a reference to an object that holds the underlying memory so that it cannot be
- * released by the garbage collector.
- */
- protected final ByteBuffer reference;
-
- // TODO life-time managment of this buffer
- //private AtomicInteger referenceCounter;
-
-
- static MessageBuffer newOffHeapBuffer(int length) {
- long address = unsafe.allocateMemory(length);
- return new MessageBuffer(address, length);
- }
-
- public static MessageBuffer newDirectBuffer(int length) {
- ByteBuffer m = ByteBuffer.allocateDirect(length);
- return newMessageBuffer(m);
- }
-
- public static MessageBuffer newBuffer(int length) {
- return newMessageBuffer(new byte[length]);
+ public static MessageBuffer allocate(int length)
+ {
+ return wrap(new byte[length]);
}
- public static MessageBuffer wrap(byte[] array) {
- return newMessageBuffer(array);
+ public static MessageBuffer wrap(byte[] array)
+ {
+ return newMessageBuffer(array, 0, array.length);
}
- public static MessageBuffer wrap(ByteBuffer bb) {
- return newMessageBuffer(bb);
- }
-
- /**
- * Creates a new MessageBuffer instance backed by ByteBuffer
- * @param bb
- * @return
- */
- private static MessageBuffer newMessageBuffer(ByteBuffer bb) {
- checkNotNull(bb);
- try {
- // We need to use reflection to create MessageBuffer instances in order to prevent TypeProfile generation for getInt method. TypeProfile will be
- // generated to resolve one of the method references when two or more classes overrides the method.
- return (MessageBuffer) mbBBConstructor.newInstance(bb);
- }
- catch(Exception e) {
- throw new RuntimeException(e);
- }
+ public static MessageBuffer wrap(byte[] array, int offset, int length)
+ {
+ return newMessageBuffer(array, offset, length);
}
/**
* Creates a new MessageBuffer instance backed by a java heap array
+ *
* @param arr
* @return
*/
- private static MessageBuffer newMessageBuffer(byte[] arr) {
+ private static MessageBuffer newMessageBuffer(byte[] arr, int off, int len)
+ {
checkNotNull(arr);
try {
- return (MessageBuffer) mbArrConstructor.newInstance(arr);
+ return (MessageBuffer) mbArrConstructor.newInstance(arr, off, len);
}
- catch(Throwable e) {
+ catch (Throwable e) {
throw new RuntimeException(e);
}
}
-
- public static void releaseBuffer(MessageBuffer buffer) {
- if(buffer.base instanceof byte[]) {
+ public static void releaseBuffer(MessageBuffer buffer)
+ {
+ if (isUniversalBuffer || buffer.base instanceof byte[]) {
// We have nothing to do. Wait until the garbage-collector collects this array object
}
- else if(buffer.base instanceof DirectBuffer) {
- ((DirectBuffer) buffer.base).cleaner().clean();
- }
else {
// Maybe cannot reach here
unsafe.freeMemory(buffer.address);
}
}
-
- /**
- * Create a MessageBuffer instance from a given memory address and length
- * @param address
- * @param length
- */
- MessageBuffer(long address, int length) {
- this.base = null;
- this.address = address;
- this.size = length;
- this.reference = null;
- }
-
- /**
- * Create a MessageBuffer instance from a given ByteBuffer instance
- * @param bb
- */
- MessageBuffer(ByteBuffer bb) {
- if(bb.isDirect()) {
- // Direct buffer or off-heap memory
- DirectBuffer db = DirectBuffer.class.cast(bb);
- this.base = null;
- this.address = db.address();
- this.size = bb.capacity();
- this.reference = bb;
- }
- else if(bb.hasArray()) {
- this.base = bb.array();
- this.address = ARRAY_BYTE_BASE_OFFSET;
- this.size = bb.array().length;
- this.reference = null;
- } else {
- throw new IllegalArgumentException("Only the array-backed ByteBuffer or DirectBuffer are supported");
- }
- }
-
-
/**
* Create a MessageBuffer instance from an java heap array
+ *
* @param arr
+ * @param offset
+ * @param length
*/
- MessageBuffer(byte[] arr) {
+ MessageBuffer(byte[] arr, int offset, int length)
+ {
this.base = arr;
- this.address = ARRAY_BYTE_BASE_OFFSET;
- this.size = arr.length;
- this.reference = null;
+ this.address = ARRAY_BYTE_BASE_OFFSET + offset;
+ this.size = length;
}
- MessageBuffer(Object base, long address, int length, ByteBuffer reference) {
+ protected MessageBuffer(Object base, long address, int length)
+ {
this.base = base;
this.address = address;
this.size = length;
- this.reference = reference;
}
/**
* byte size of the buffer
+ *
* @return
*/
- public int size() { return size; }
-
+ public int size()
+ {
+ return size;
+ }
- public MessageBuffer slice(int offset, int length) {
+ public MessageBuffer slice(int offset, int length)
+ {
// TODO ensure deleting this slice does not collapse this MessageBuffer
- if(offset == 0 && length == size())
+ if (offset == 0 && length == size()) {
return this;
+ }
else {
checkArgument(offset + length <= size());
- return new MessageBuffer(base, address + offset, length, reference);
+ return new MessageBuffer(base, address + offset, length);
}
}
- public byte getByte(int index) {
+ public byte getByte(int index)
+ {
return unsafe.getByte(base, address + index);
}
- public boolean getBoolean(int index) {
+ public boolean getBoolean(int index)
+ {
return unsafe.getBoolean(base, address + index);
}
- public short getShort(int index) {
+ public short getShort(int index)
+ {
short v = unsafe.getShort(base, address + index);
return Short.reverseBytes(v);
}
/**
* Read a big-endian int value at the specified index
+ *
* @param index
* @return
*/
- public int getInt(int index) {
+ public int getInt(int index)
+ {
// Reading little-endian value
int i = unsafe.getInt(base, address + index);
// Reversing the endian
return Integer.reverseBytes(i);
}
- public float getFloat(int index) {
+ public float getFloat(int index)
+ {
return Float.intBitsToFloat(getInt(index));
}
- public long getLong(int index) {
+ public long getLong(int index)
+ {
long l = unsafe.getLong(base, address + index);
return Long.reverseBytes(l);
}
- public double getDouble(int index) {
+ public double getDouble(int index)
+ {
return Double.longBitsToDouble(getLong(index));
}
- public void getBytes(int index, byte[] dst, int dstOffset, int length) {
- unsafe.copyMemory(base, address+index, dst, ARRAY_BYTE_BASE_OFFSET + dstOffset, length);
+ public void getBytes(int index, byte[] dst, int dstOffset, int length)
+ {
+ unsafe.copyMemory(base, address + index, dst, ARRAY_BYTE_BASE_OFFSET + dstOffset, length);
}
- public void getBytes(int index, int len, ByteBuffer dst) {
- if(dst.remaining() > len)
+ public void getBytes(int index, int len, ByteBuffer dst)
+ {
+ if (dst.remaining() < len) {
throw new BufferOverflowException();
- ByteBuffer src = toByteBuffer(index, len);
+ }
+ ByteBuffer src = sliceAsByteBuffer(index, len);
dst.put(src);
}
-
- public void putByte(int index, byte v) {
+ public void putByte(int index, byte v)
+ {
unsafe.putByte(base, address + index, v);
}
- public void putBoolean(int index, boolean v) {
+ public void putBoolean(int index, boolean v)
+ {
unsafe.putBoolean(base, address + index, v);
}
- public void putShort(int index, short v) {
+ public void putShort(int index, short v)
+ {
v = Short.reverseBytes(v);
unsafe.putShort(base, address + index, v);
}
/**
* Write a big-endian integer value to the memory
+ *
* @param index
* @param v
*/
- public void putInt(int index, int v){
+ public void putInt(int index, int v)
+ {
// Reversing the endian
v = Integer.reverseBytes(v);
unsafe.putInt(base, address + index, v);
}
- public void putFloat(int index, float v) {
+ public void putFloat(int index, float v)
+ {
putInt(index, Float.floatToRawIntBits(v));
}
- public void putLong(int index, long l) {
+ public void putLong(int index, long l)
+ {
// Reversing the endian
l = Long.reverseBytes(l);
unsafe.putLong(base, address + index, l);
}
- public void putDouble(int index, double v) {
+ public void putDouble(int index, double v)
+ {
putLong(index, Double.doubleToRawLongBits(v));
}
- public void putBytes(int index, byte[] src, int srcOffset, int length) {
- unsafe.copyMemory(src, ARRAY_BYTE_BASE_OFFSET + srcOffset, base, address+index, length);
+ public void putBytes(int index, byte[] src, int srcOffset, int length)
+ {
+ unsafe.copyMemory(src, ARRAY_BYTE_BASE_OFFSET + srcOffset, base, address + index, length);
}
- public void putByteBuffer(int index, ByteBuffer src, int len) {
- assert(len <= src.remaining());
+ public void putByteBuffer(int index, ByteBuffer src, int len)
+ {
+ assert (len <= src.remaining());
+ assert (!isUniversalBuffer);
- if(src.isDirect()) {
- DirectBuffer db = (DirectBuffer) src;
- unsafe.copyMemory(null, db.address() + src.position(), base, address+index, len);
- } else if(src.hasArray()) {
+ if (src.isDirect()) {
+ unsafe.copyMemory(null, DirectBufferAccess.getAddress(src) + src.position(), base, address + index, len);
+ src.position(src.position() + len);
+ }
+ else if (src.hasArray()) {
byte[] srcArray = src.array();
- unsafe.copyMemory(srcArray, ARRAY_BYTE_BASE_OFFSET + src.position(), base, address+index, len);
- } else {
- if(base != null) {
- src.get((byte []) base, index, len);
+ unsafe.copyMemory(srcArray, ARRAY_BYTE_BASE_OFFSET + src.position(), base, address + index, len);
+ src.position(src.position() + len);
+ }
+ else {
+ if (base != null) {
+ src.get((byte[]) base, index, len);
}
else {
- for(int i=0; i
+ * When this method is called, the formally allocated buffer can be safely discarded.
+ *
+ * @return the next MessageBuffer, or return null if no more buffer is available.
* @throws IOException when error occurred when reading the data
*/
- public MessageBuffer next() throws IOException;
-
-
+ MessageBuffer next()
+ throws IOException;
}
-
-
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBufferOutput.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBufferOutput.java
index 4b529c44f..024414bae 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBufferOutput.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBufferOutput.java
@@ -1,33 +1,76 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer;
-
import java.io.Closeable;
import java.io.IOException;
+import java.io.Flushable;
/**
- * Provides a sequence of MessageBuffers for packing the input data
+ * Provides a buffered output stream for packing objects
*/
-public interface MessageBufferOutput extends Closeable {
-
+public interface MessageBufferOutput
+ extends Closeable, Flushable
+{
/**
- * Retrieves the next buffer for writing message packed data
+ * Allocates the next buffer for writing message packed data.
+ * If the previously allocated buffer is not flushed yet, this next method should discard
+ * it without writing it.
*
- * @param bufferSize the buffer size to retrieve
+ * @param minimumSize the mimium required buffer size to allocate
* @return
* @throws IOException
*/
- public MessageBuffer next(int bufferSize) throws IOException;
+ MessageBuffer next(int minimumSize)
+ throws IOException;
/**
- * Output the buffer contents. If you need to output a part of the
- * buffer use {@link MessageBuffer#slice(int, int)}
- * @param buf
+ * Flushes the previously allocated buffer.
+ * This method is not always called because next method also flushes previously allocated buffer.
+ * This method is called when write method is called or application wants to control the timing of flush.
+ *
+ * @param length the size of buffer to flush
* @throws IOException
*/
- public void flush(MessageBuffer buf) throws IOException;
-
-}
-
-
+ void writeBuffer(int length)
+ throws IOException;
+ /**
+ * Writes an external payload data.
+ * This method should follow semantics of OutputStream.
+ *
+ * @param buffer the data to write
+ * @param offset the start offset in the data
+ * @param length the number of bytes to write
+ * @return
+ * @throws IOException
+ */
+ void write(byte[] buffer, int offset, int length)
+ throws IOException;
+ /**
+ * Writes an external payload data.
+ * This buffer is given - this MessageBufferOutput owns the buffer and may modify contents of the buffer. Contents of this buffer won't be modified by the caller.
+ *
+ * @param buffer the data to add
+ * @param offset the start offset in the data
+ * @param length the number of bytes to add
+ * @return
+ * @throws IOException
+ */
+ void add(byte[] buffer, int offset, int length)
+ throws IOException;
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBufferU.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBufferU.java
new file mode 100644
index 000000000..1e8783738
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/core/buffer/MessageBufferU.java
@@ -0,0 +1,252 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer;
+
+import java.nio.ByteBuffer;
+
+import static org.msgpack.core.Preconditions.checkArgument;
+
+/**
+ * Universal MessageBuffer implementation supporting Java6 and Android.
+ * This buffer always uses ByteBuffer-based memory access
+ */
+public class MessageBufferU
+ extends MessageBuffer
+{
+ private final ByteBuffer wrap;
+
+ MessageBufferU(byte[] arr, int offset, int length)
+ {
+ super(arr, offset, length);
+ ByteBuffer bb = ByteBuffer.wrap(arr);
+ bb.position(offset);
+ bb.limit(offset + length);
+ this.wrap = bb.slice();
+ }
+
+ private MessageBufferU(Object base, long address, int length, ByteBuffer wrap)
+ {
+ super(base, address, length);
+ this.wrap = wrap;
+ }
+
+ @Override
+ public MessageBufferU slice(int offset, int length)
+ {
+ if (offset == 0 && length == size()) {
+ return this;
+ }
+ else {
+ checkArgument(offset + length <= size());
+ try {
+ wrap.position(offset);
+ wrap.limit(offset + length);
+ return new MessageBufferU(base, address + offset, length, wrap.slice());
+ }
+ finally {
+ resetBufferPosition();
+ }
+ }
+ }
+
+ private void resetBufferPosition()
+ {
+ wrap.position(0);
+ wrap.limit(size);
+ }
+
+ @Override
+ public byte getByte(int index)
+ {
+ return wrap.get(index);
+ }
+
+ @Override
+ public boolean getBoolean(int index)
+ {
+ return wrap.get(index) != 0;
+ }
+
+ @Override
+ public short getShort(int index)
+ {
+ return wrap.getShort(index);
+ }
+
+ @Override
+ public int getInt(int index)
+ {
+ return wrap.getInt(index);
+ }
+
+ @Override
+ public float getFloat(int index)
+ {
+ return wrap.getFloat(index);
+ }
+
+ @Override
+ public long getLong(int index)
+ {
+ return wrap.getLong(index);
+ }
+
+ @Override
+ public double getDouble(int index)
+ {
+ return wrap.getDouble(index);
+ }
+
+ @Override
+ public void getBytes(int index, int len, ByteBuffer dst)
+ {
+ try {
+ wrap.position(index);
+ wrap.limit(index + len);
+ dst.put(wrap);
+ }
+ finally {
+ resetBufferPosition();
+ }
+ }
+
+ @Override
+ public void putByte(int index, byte v)
+ {
+ wrap.put(index, v);
+ }
+
+ @Override
+ public void putBoolean(int index, boolean v)
+ {
+ wrap.put(index, v ? (byte) 1 : (byte) 0);
+ }
+
+ @Override
+ public void putShort(int index, short v)
+ {
+ wrap.putShort(index, v);
+ }
+
+ @Override
+ public void putInt(int index, int v)
+ {
+ wrap.putInt(index, v);
+ }
+
+ @Override
+ public void putFloat(int index, float v)
+ {
+ wrap.putFloat(index, v);
+ }
+
+ @Override
+ public void putLong(int index, long l)
+ {
+ wrap.putLong(index, l);
+ }
+
+ @Override
+ public void putDouble(int index, double v)
+ {
+ wrap.putDouble(index, v);
+ }
+
+ @Override
+ public ByteBuffer sliceAsByteBuffer(int index, int length)
+ {
+ try {
+ wrap.position(index);
+ wrap.limit(index + length);
+ return wrap.slice();
+ }
+ finally {
+ resetBufferPosition();
+ }
+ }
+
+ @Override
+ public ByteBuffer sliceAsByteBuffer()
+ {
+ return sliceAsByteBuffer(0, size);
+ }
+
+ @Override
+ public void getBytes(int index, byte[] dst, int dstOffset, int length)
+ {
+ try {
+ wrap.position(index);
+ wrap.get(dst, dstOffset, length);
+ }
+ finally {
+ resetBufferPosition();
+ }
+ }
+
+ @Override
+ public void putByteBuffer(int index, ByteBuffer src, int len)
+ {
+ assert (len <= src.remaining());
+
+ if (src.hasArray()) {
+ putBytes(index, src.array(), src.position() + src.arrayOffset(), len);
+ src.position(src.position() + len);
+ }
+ else {
+ int prevSrcLimit = src.limit();
+ try {
+ src.limit(src.position() + len);
+ wrap.position(index);
+ wrap.put(src);
+ }
+ finally {
+ src.limit(prevSrcLimit);
+ }
+ }
+ }
+
+ @Override
+ public void putBytes(int index, byte[] src, int srcOffset, int length)
+ {
+ try {
+ wrap.position(index);
+ wrap.put(src, srcOffset, length);
+ }
+ finally {
+ resetBufferPosition();
+ }
+ }
+
+ @Override
+ public void copyTo(int index, MessageBuffer dst, int offset, int length)
+ {
+ try {
+ wrap.position(index);
+ dst.putByteBuffer(offset, wrap, length);
+ }
+ finally {
+ resetBufferPosition();
+ }
+ }
+
+ @Override
+ public byte[] toByteArray()
+ {
+ byte[] b = new byte[size()];
+ getBytes(0, b, 0, b.length);
+ return b;
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/buffer/OutputStreamBufferOutput.java b/msgpack-core/src/main/java/org/msgpack/core/buffer/OutputStreamBufferOutput.java
index 902b63039..08fd3960b 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/buffer/OutputStreamBufferOutput.java
+++ b/msgpack-core/src/main/java/org/msgpack/core/buffer/OutputStreamBufferOutput.java
@@ -1,3 +1,18 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer;
import java.io.IOException;
@@ -8,53 +23,79 @@
/**
* MessageBufferOutput adapter for {@link java.io.OutputStream}.
*/
-public class OutputStreamBufferOutput implements MessageBufferOutput {
-
+public class OutputStreamBufferOutput
+ implements MessageBufferOutput
+{
private OutputStream out;
private MessageBuffer buffer;
- private byte[] tmpBuf;
- public OutputStreamBufferOutput(OutputStream out) {
+ public OutputStreamBufferOutput(OutputStream out)
+ {
+ this(out, 8192);
+ }
+
+ public OutputStreamBufferOutput(OutputStream out, int bufferSize)
+ {
this.out = checkNotNull(out, "output is null");
+ this.buffer = MessageBuffer.allocate(bufferSize);
}
- public void reset(OutputStream out) throws IOException {
- out.close();
+ /**
+ * Reset Stream. This method doesn't close the old stream.
+ *
+ * @param out new stream
+ * @return the old stream
+ */
+ public OutputStream reset(OutputStream out)
+ throws IOException
+ {
+ OutputStream old = this.out;
this.out = out;
+ return old;
}
@Override
- public MessageBuffer next(int bufferSize) throws IOException {
- if(buffer == null || buffer.size != bufferSize) {
- return buffer = MessageBuffer.newBuffer(bufferSize);
- }
- else {
- return buffer;
+ public MessageBuffer next(int mimimumSize)
+ throws IOException
+ {
+ if (buffer.size() < mimimumSize) {
+ buffer = MessageBuffer.allocate(mimimumSize);
}
+ return buffer;
}
@Override
- public void flush(MessageBuffer buf) throws IOException {
- int writeLen = buf.size();
- if(buf.hasArray()) {
- out.write(buf.getArray(), buf.offset(), writeLen);
- }
- else {
- if(tmpBuf == null || tmpBuf.length < writeLen) {
- tmpBuf = new byte[writeLen];
- }
- buf.getBytes(0, tmpBuf, 0, writeLen);
- out.write(tmpBuf, 0, writeLen);
- }
+ public void writeBuffer(int length)
+ throws IOException
+ {
+ write(buffer.array(), buffer.arrayOffset(), length);
}
@Override
- public void close() throws IOException {
- try {
- out.flush();
- }
- finally {
- out.close();
- }
+ public void write(byte[] buffer, int offset, int length)
+ throws IOException
+ {
+ out.write(buffer, offset, length);
+ }
+
+ @Override
+ public void add(byte[] buffer, int offset, int length)
+ throws IOException
+ {
+ write(buffer, offset, length);
+ }
+
+ @Override
+ public void close()
+ throws IOException
+ {
+ out.close();
+ }
+
+ @Override
+ public void flush()
+ throws IOException
+ {
+ out.flush();
}
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/AbstractValueVisitor.java b/msgpack-core/src/main/java/org/msgpack/value/AbstractValueVisitor.java
deleted file mode 100644
index 06697d3c5..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/AbstractValueVisitor.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.msgpack.value;
-
-/**
- * Empty visitor that does nothing
- */
-public class AbstractValueVisitor implements ValueVisitor {
-
- @Override
- public void visitNil() {
-
- }
- @Override
- public void visitBoolean(boolean v) {
-
- }
- @Override
- public void visitInteger(IntegerValue v) {
-
- }
- @Override
- public void visitFloat(FloatValue v) {
-
- }
- @Override
- public void visitBinary(BinaryValue v) {
-
- }
- @Override
- public void visitString(StringValue v) {
-
- }
- @Override
- public void visitArray(ArrayValue v) {
-
- }
- @Override
- public void visitMap(MapValue v) {
-
- }
- @Override
- public void visitExtended(ExtendedValue v) {
-
- }
- @Override
- public void onError(Exception e) {
-
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ArrayCursor.java b/msgpack-core/src/main/java/org/msgpack/value/ArrayCursor.java
deleted file mode 100644
index d491ba9c8..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/ArrayCursor.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.msgpack.value;
-
-import java.util.Iterator;
-
-/**
- * Created on 6/16/14.
- */
-public interface ArrayCursor extends ValueRef, Iterable {
- public int size();
-
- public boolean hasNext();
- public ValueRef next();
- public void skip();
-
- /**
- * Skips all of the remaining values
- */
- public void skipAll();
-
- public Iterator iterator();
-
- public ArrayValue toValue();
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ArrayValue.java b/msgpack-core/src/main/java/org/msgpack/value/ArrayValue.java
index 570b533af..4d7a6e8c2 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/ArrayValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/ArrayValue.java
@@ -1,23 +1,57 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
import java.util.Iterator;
import java.util.List;
/**
- * Value interface for array type data.
+ * The interface {@code ArrayValue} represents MessagePack's Array type.
*
- * Implementation note: We do not implement List interface here, because
- * we cannot reuse AbstractList and AbstractValue implementations simultaneously since
- * Java does not support mixin of classes. Instead, it provides {@link #iterator} or
- * {@link #toValueArray()} methods to traverse the array contents.
+ * MessagePack's Array type can represent sequence of values.
*/
-public interface ArrayValue extends Value, ArrayCursor {
+public interface ArrayValue
+ extends Value, Iterable
+{
+ /**
+ * Returns number of elements in this array.
+ */
+ int size();
- public Value[] toValueArray();
+ /**
+ * Returns the element at the specified position in this array.
+ *
+ * @throws IndexOutOfBoundsException If the index is out of range
+ * (index < 0 || index >= size() )
+ */
+ Value get(int index);
- public Value get(int index);
- public Value apply(int index);
+ /**
+ * Returns the element at the specified position in this array.
+ * This method returns an ImmutableNilValue if the index is out of range.
+ */
+ Value getOrNilValue(int index);
- public ArrayValue toValue();
+ /**
+ * Returns an iterator over elements.
+ */
+ Iterator iterator();
+ /**
+ * Returns the value as {@code List}.
+ */
+ List list();
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/BinaryValue.java b/msgpack-core/src/main/java/org/msgpack/value/BinaryValue.java
index 808de6f9b..c66f7a67c 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/BinaryValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/BinaryValue.java
@@ -1,8 +1,28 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
/**
-* Created on 5/30/14.
-*/
-public interface BinaryValue extends RawValue {
- BinaryValue toValue();
+ * The interface {@code BinaryValue} represents MessagePack's Binary type.
+ *
+ * MessagePack's Binary type can represent a byte array at most 264 -1 bytes.
+ *
+ * @see org.msgpack.value.RawValue
+ */
+public interface BinaryValue
+ extends RawValue
+{
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/BooleanValue.java b/msgpack-core/src/main/java/org/msgpack/value/BooleanValue.java
index 2d517469e..6ccf5c9ef 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/BooleanValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/BooleanValue.java
@@ -1,9 +1,30 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
/**
-* Created on 5/30/14.
-*/
-public interface BooleanValue extends Value {
- public boolean toBoolean();
- public BooleanValue toValue();
+ * The interface {@code BooleanValue} represents MessagePack's Boolean type.
+ *
+ * MessagePack's Boolean type can represent {@code true} or {@code false}.
+ */
+public interface BooleanValue
+ extends Value
+{
+ /**
+ * Returns the value as a {@code boolean}.
+ */
+ boolean getBoolean();
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/Cursor.java b/msgpack-core/src/main/java/org/msgpack/value/Cursor.java
deleted file mode 100644
index 09fa50e5f..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/Cursor.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.msgpack.value;
-
-
-import java.io.Closeable;
-import java.util.Iterator;
-
-/**
- * Cursor for traversing a stream of message-packed values
- */
-public interface Cursor extends Iterator, Closeable {
-
- /**
- * Tests whether there is a next element.
- * @return true if there is a next element, or false if there is no more element.
- */
- public boolean hasNext();
-
- /**
- * Returns a reference to the value, then proceeds the cursor.
- * The returned reference is valid until {@link #hasNext()} is called.
- * @return
- */
- public ValueRef nextRef();
-
- /**
- * Returns the materialized value of the referenced value, then proceeds the cursor.
- * @return
- */
- public Value next();
-
- /**
- * Skip reading the current value.
- */
- public void skip();
-
- /**
- * Returns the number of the read bytes
- * @return the number of the read bytes
- */
- public long getReadBytes();
-
- public static interface Function {
- public Out apply(Value input) throws Exception;
- }
-
- /**
- * Applies a function f to the referenced value, then returns the result of the function.
- * @param f a function that receives the referenced value.
- * @param the result type of the function
- * @return the result of the function
- */
- public Out apply(Function f);
-
- public boolean isNilValue();
- public boolean isBooleanValue();
- public boolean isNumberValue();
- public boolean isIntegerValue();
- public boolean isFloatValue();
- public boolean isBinaryValue();
- public boolean isStringValue();
- public boolean isRawValue();
- public boolean isArrayValue();
- public boolean isMapValue();
- public boolean isExtendedValue();
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ExtendedValue.java b/msgpack-core/src/main/java/org/msgpack/value/ExtendedValue.java
deleted file mode 100644
index 9d4b47a16..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/ExtendedValue.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.msgpack.value;
-
-/**
-* Created on 5/30/14.
-*/
-public interface ExtendedValue extends RawValue {
- public int getExtType();
- public ExtendedValue toValue();
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ValueVisitor.java b/msgpack-core/src/main/java/org/msgpack/value/ExtensionValue.java
similarity index 55%
rename from msgpack-core/src/main/java/org/msgpack/value/ValueVisitor.java
rename to msgpack-core/src/main/java/org/msgpack/value/ExtensionValue.java
index 22436d30a..5a076ecbc 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/ValueVisitor.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/ExtensionValue.java
@@ -16,19 +16,17 @@
package org.msgpack.value;
/**
- * Interface for implementing the visitor pattern on message-packed values
+ * The interface {@code ExtensionValue} represents MessagePack's Extension type.
+ *
+ * MessagePack's Extension type can represent represents a tuple of type information and a byte array where type information is an
+ * integer whose meaning is defined by applications.
+ *
+ * As the type information, applications can use 0 to 127 as the application-specific types. -1 to -128 is reserved for MessagePack's future extension.
*/
-public interface ValueVisitor {
+public interface ExtensionValue
+ extends Value
+{
+ byte getType();
- public void visitNil();
- public void visitBoolean(boolean v);
- public void visitInteger(IntegerValue v);
- public void visitFloat(FloatValue v);
- public void visitBinary(BinaryValue v);
- public void visitString(StringValue v);
- public void visitArray(ArrayValue v);
- public void visitMap(MapValue v);
- public void visitExtended(ExtendedValue v);
-
- public void onError(Exception e);
+ byte[] getData();
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/FloatValue.java b/msgpack-core/src/main/java/org/msgpack/value/FloatValue.java
index 3e5dda745..dbaf73a18 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/FloatValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/FloatValue.java
@@ -1,8 +1,28 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
/**
-* Created on 5/30/14.
-*/
-public interface FloatValue extends NumberValue {
- FloatValue toValue();
+ * The interface {@code FloatValue} represents MessagePack's Float type.
+ *
+ * MessagePack's Float type can represent IEEE 754 double precision floating point numbers including NaN and infinity. This is same with Java's {@code double} type.
+ *
+ * @see org.msgpack.value.NumberValue
+ */
+public interface FloatValue
+ extends NumberValue
+{
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableArrayValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableArrayValue.java
new file mode 100644
index 000000000..9301c2eb8
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableArrayValue.java
@@ -0,0 +1,35 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+import java.util.Iterator;
+import java.util.List;
+
+public interface ImmutableArrayValue
+ extends ArrayValue, ImmutableValue
+{
+ /**
+ * Returns an iterator over elements.
+ * Returned Iterator does not support {@code remove()} method since the value is immutable.
+ */
+ Iterator iterator();
+
+ /**
+ * Returns the value as {@code List}.
+ * Returned List is immutable. It does not support {@code put()}, {@code clear()}, or other methods that modify the value.
+ */
+ List list();
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableBinaryValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableBinaryValue.java
new file mode 100644
index 000000000..475241a57
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableBinaryValue.java
@@ -0,0 +1,21 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableBinaryValue
+ extends BinaryValue, ImmutableRawValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableBooleanValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableBooleanValue.java
new file mode 100644
index 000000000..dd2afad43
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableBooleanValue.java
@@ -0,0 +1,21 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableBooleanValue
+ extends BooleanValue, ImmutableValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableExtensionValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableExtensionValue.java
new file mode 100644
index 000000000..5e984db05
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableExtensionValue.java
@@ -0,0 +1,21 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableExtensionValue
+ extends ExtensionValue, ImmutableValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableFloatValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableFloatValue.java
new file mode 100644
index 000000000..7105483d1
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableFloatValue.java
@@ -0,0 +1,21 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableFloatValue
+ extends FloatValue, ImmutableNumberValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableIntegerValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableIntegerValue.java
new file mode 100644
index 000000000..3482583ff
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableIntegerValue.java
@@ -0,0 +1,21 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableIntegerValue
+ extends IntegerValue, ImmutableNumberValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableMapValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableMapValue.java
new file mode 100644
index 000000000..cc3122f03
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableMapValue.java
@@ -0,0 +1,21 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableMapValue
+ extends MapValue, ImmutableValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableNilValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableNilValue.java
new file mode 100644
index 000000000..8a7857287
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableNilValue.java
@@ -0,0 +1,21 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableNilValue
+ extends NilValue, ImmutableValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableNumberValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableNumberValue.java
new file mode 100644
index 000000000..42afcf304
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableNumberValue.java
@@ -0,0 +1,21 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableNumberValue
+ extends NumberValue, ImmutableValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableRawValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableRawValue.java
new file mode 100644
index 000000000..36698dbeb
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableRawValue.java
@@ -0,0 +1,21 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableRawValue
+ extends RawValue, ImmutableValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableStringValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableStringValue.java
new file mode 100644
index 000000000..6e3f95360
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableStringValue.java
@@ -0,0 +1,21 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableStringValue
+ extends StringValue, ImmutableRawValue
+{
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ImmutableValue.java b/msgpack-core/src/main/java/org/msgpack/value/ImmutableValue.java
new file mode 100644
index 000000000..6a5740029
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/ImmutableValue.java
@@ -0,0 +1,47 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+public interface ImmutableValue
+ extends Value
+{
+ @Override
+ public ImmutableNilValue asNilValue();
+
+ @Override
+ public ImmutableBooleanValue asBooleanValue();
+
+ @Override
+ public ImmutableIntegerValue asIntegerValue();
+
+ @Override
+ public ImmutableFloatValue asFloatValue();
+
+ @Override
+ public ImmutableArrayValue asArrayValue();
+
+ @Override
+ public ImmutableMapValue asMapValue();
+
+ @Override
+ public ImmutableRawValue asRawValue();
+
+ @Override
+ public ImmutableBinaryValue asBinaryValue();
+
+ @Override
+ public ImmutableStringValue asStringValue();
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/IntegerValue.java b/msgpack-core/src/main/java/org/msgpack/value/IntegerValue.java
index 4299b42ad..8480751c4 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/IntegerValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/IntegerValue.java
@@ -1,8 +1,88 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+import org.msgpack.core.MessageFormat;
+
+import java.math.BigInteger;
+
/**
-* Created on 5/30/14.
-*/
-public interface IntegerValue extends NumberValue {
- IntegerValue toValue();
+ * The interface {@code IntegerValue} represents MessagePack's Integer type.
+ *
+ * MessagePack's Integer type can represent from -263 to 264 -1.
+ */
+public interface IntegerValue
+ extends NumberValue
+{
+ /**
+ * Returns true if the value is in the range of [-27 to 27 -1].
+ */
+ boolean isInByteRange();
+
+ /**
+ * Returns true if the value is in the range of [-215 to 215 -1]
+ */
+ boolean isInShortRange();
+
+ /**
+ * Returns true if the value is in the range of [-231 to 231 -1]
+ */
+ boolean isInIntRange();
+
+ /**
+ * Returns true if the value is in the range of [-263 to 263 -1]
+ */
+ boolean isInLongRange();
+
+ /**
+ * Returns the most succinct MessageFormat type to represent this integer value.
+ * @return
+ */
+ MessageFormat mostSuccinctMessageFormat();
+
+ /**
+ * Returns the value as a {@code byte}, otherwise throws an exception.
+ *
+ * @throws MessageIntegerOverflowException If the value does not fit in the range of {@code byte} type.
+ */
+ byte asByte();
+
+ /**
+ * Returns the value as a {@code short}, otherwise throws an exception.
+ *
+ * @throws MessageIntegerOverflowException If the value does not fit in the range of {@code short} type.
+ */
+ short asShort();
+
+ /**
+ * Returns the value as an {@code int}, otherwise throws an exception.
+ *
+ * @throws MessageIntegerOverflowException If the value does not fit in the range of {@code int} type.
+ */
+ int asInt();
+
+ /**
+ * Returns the value as a {@code long}, otherwise throws an exception.
+ *
+ * @throws MessageIntegerOverflowException If the value does not fit in the range of {@code long} type.
+ */
+ long asLong();
+
+ /**
+ * Returns the value as a {@code BigInteger}.
+ */
+ BigInteger asBigInteger();
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/MapCursor.java b/msgpack-core/src/main/java/org/msgpack/value/MapCursor.java
deleted file mode 100644
index beae8cd54..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/MapCursor.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.msgpack.value;
-
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * Cursor for traversing map value entries. This cursor reports a sequence of key and value pairs.
- */
-public interface MapCursor extends ValueRef {
- public int size();
-
- /**
- * Test whether this cursor can point to a next key or value.
- * @return
- */
- public boolean hasNext();
-
- /**
- * Retrieves a reference to the next key or value.
- * @return
- */
- public ValueRef nextKeyOrValue();
-
- /**
- * Skips a next key or value
- */
- public void skipKeyOrValue();
-
- /**
- * Skips all of the remaining keys and values.
- */
- public void skipAll();
-
- MapValue toValue();
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/MapValue.java b/msgpack-core/src/main/java/org/msgpack/value/MapValue.java
index ea67fdad6..fa7f55c8d 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/MapValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/MapValue.java
@@ -1,13 +1,54 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+import java.util.Collection;
import java.util.Map;
+import java.util.Set;
/**
-* Created on 5/30/14.
-*/
-public interface MapValue extends Value, MapCursor {
- public Value[] toKeyValueSeq();
- public Map toMap();
+ * The interface {@code ArrayValue} represents MessagePack's Map type.
+ *
+ * MessagePack's Map type can represent sequence of key-value pairs.
+ */
+public interface MapValue
+ extends Value
+{
+ /**
+ * Returns number of key-value pairs in this array.
+ */
+ int size();
- public MapValue toValue();
+ Set keySet();
+
+ Set> entrySet();
+
+ Collection values();
+
+ /**
+ * Returns the value as {@code Map}.
+ */
+ Map map();
+
+ /**
+ * Returns the key-value pairs as an array of {@code Value}.
+ *
+ * Odd elements are keys. Next element of an odd element is a value corresponding to the key.
+ *
+ * For example, if this value represents {"k1": "v1", "k2": "v2"}, this method returns ["k1", "v1", "k2", "v2"].
+ */
+ Value[] getKeyValueArray();
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/NilValue.java b/msgpack-core/src/main/java/org/msgpack/value/NilValue.java
index 4c8c39beb..8f5835001 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/NilValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/NilValue.java
@@ -1,8 +1,24 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
/**
- * References to values
+ * The interface {@code NilValue} represents MessagePack's Nil type.
*/
-public interface NilValue extends Value {
- NilValue toValue();
+public interface NilValue
+ extends Value
+{
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/NumberValue.java b/msgpack-core/src/main/java/org/msgpack/value/NumberValue.java
index 38952fcc5..3e5bd75e0 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/NumberValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/NumberValue.java
@@ -1,106 +1,65 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
-import org.msgpack.core.MessageOverflowException;
-
import java.math.BigInteger;
/**
-* Created on 5/30/14.
-*/
-public interface NumberValue extends Value {
+ * The base interface {@code NumberValue} of {@code IntegerValue} and {@code FloatValue}. To extract primitive type values, call toXXX methods, which may lose some information by rounding or truncation.
+ *
+ * @see org.msgpack.value.IntegerValue
+ * @see org.msgpack.value.FloatValue
+ */
+public interface NumberValue
+ extends Value
+{
+ /**
+ * Represent this value as a byte value, which may involve rounding or truncation of the original value.
+ * the value.
+ */
+ byte toByte();
/**
- * Check whether this value is a valid byte value.
- * @return true if this value has no fractional part, and is within the range of {@link Byte#MIN_VALUE} and {@link Byte#MAX_VALUE}; otherwise returns false
+ * Represent this value as a short value, which may involve rounding or truncation of the original value.
*/
- public boolean isValidByte();
+ short toShort();
/**
- * Check whether this value is a valid short value.
- * @return true if this value has no fractional part, and is within the range of {@link Short#MIN_VALUE} and {@link Short#MAX_VALUE}; otherwise returns false
+ * Represent this value as an int value, which may involve rounding or truncation of the original value.
+ * value.
*/
- public boolean isValidShort();
+ int toInt();
/**
- * Check whether this value is a valid integer value.
- * @return true if this value has no fractional part, and is within the range of {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}; otherwise returns false
+ * Represent this value as a long value, which may involve rounding or truncation of the original value.
*/
- public boolean isValidInt();
+ long toLong();
/**
- * Check whether this value is a valid long value.
- * @return true if this value has no fractional part, and is within the range of {@link Long#MIN_VALUE} and {@link Long#MAX_VALUE}; otherwise returns false
+ * Represent this value as a BigInteger, which may involve rounding or truncation of the original value.
*/
- public boolean isValidLong();
+ BigInteger toBigInteger();
/**
- * Returns true if this number has no decimal component
- * @return true if this number has no decimal component, otherwise false (float, double values);
+ * Represent this value as a 32-bit float value, which may involve rounding or truncation of the original value.
*/
- public boolean isWhole();
+ float toFloat();
/**
- * Convert this value into a byte value. If this value is not within the range of Byte value, it will truncate or round the value.
- */
- public byte toByte();
- /**
- * Convert this value into a short value. If this value is not within the range of Short value, it will truncate or round the value.
- */
- public short toShort();
- /**
- * Convert this value into an int value. If this value is not within the range of Int value, it will truncate or round the value.
- */
- public int toInt();
- /**
- * Convert this value into a long value. If this value is not within the range of Long value, it will truncate or round the value.
+ * Represent this value as a 64-bit double value, which may involve rounding or truncation of the original value.
*/
- public long toLong();
- /**
- * Convert this value into a BigInteger
- */
- public BigInteger toBigInteger();
- /**
- * Convert this value into a float value
- */
- public float toFloat();
- /**
- * Convert this value into a double value
- */
- public double toDouble();
-
- /**
- * Convert this value into a byte value. If this value is not within the range of Byte value, it throws an exception.
- * @return
- * @throws org.msgpack.core.MessageOverflowException when the value is not within the range of {@link Byte#MIN_VALUE} and {@link Byte#MAX_VALUE};
- */
- public byte asByte() throws MessageOverflowException;
-
- /**
- * Convert this value into a short value. If this value is not within the range of Short value, it throws an exception.
- * @return
- * @throws org.msgpack.core.MessageOverflowException when the value is not within the range of {@link Short#MIN_VALUE} and {@link Short#MAX_VALUE}
- */
- public short asShort() throws MessageOverflowException;
-
- /**
- * Convert this value into an int value. If this value is not within the range of Integer value, it throws an exception.
- * @return
- * @throws org.msgpack.core.MessageOverflowException when the value is not within the range of {@link Integer#MIN_VALUE} and {@link Integer#MAX_VALUE}
- */
- public int asInt() throws MessageOverflowException;
-
- /**
- * Convert this value into a long value. If this value is not within the range of Long value, it throws an exception.
- * @return
- * @throws org.msgpack.core.MessageOverflowException when the value is not within the range of {@link Long#MIN_VALUE} and {@link Long#MAX_VALUE}
- */
- public long asLong() throws MessageOverflowException;
-
- /**
- * Convert this value into a BigInteger value.
- * @return
- * @throws org.msgpack.core.MessageOverflowException
- */
- public BigInteger asBigInteger() throws MessageOverflowException;
-
+ double toDouble();
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/RawValue.java b/msgpack-core/src/main/java/org/msgpack/value/RawValue.java
index 720ecbd36..8857b5340 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/RawValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/RawValue.java
@@ -1,20 +1,61 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
-import org.msgpack.core.MessageStringCodingException;
-import org.msgpack.core.buffer.MessageBuffer;
-
import java.nio.ByteBuffer;
/**
- * Base type of StringValue, BinaryValue and ExtendedValue
+ * The interface {@code RawValue} represents MessagePack's Raw type, which means Binary or String type.
+ *
+ * MessagePack's Raw type can represent a byte array at most 264 -1 bytes.
+ *
+ * @see org.msgpack.value.StringValue
+ * @see org.msgpack.value.BinaryValue
*/
-public interface RawValue extends Value {
- public byte[] toByteArray();
- public ByteBuffer toByteBuffer();
- public MessageBuffer toMessageBuffer();
+public interface RawValue
+ extends Value
+{
+ /**
+ * Returns the value as {@code byte[]}.
+ *
+ * This method copies the byte array.
+ */
+ byte[] asByteArray();
+
+ /**
+ * Returns the value as {@code ByteBuffer}.
+ *
+ * Returned ByteBuffer is read-only. See {@code#asReadOnlyBuffer()}.
+ * This method doesn't copy the byte array as much as possible.
+ */
+ ByteBuffer asByteBuffer();
- @Override
- public String toString();
+ /**
+ * Returns the value as {@code String}.
+ *
+ * This method throws an exception if the value includes invalid UTF-8 byte sequence.
+ *
+ * @throws MessageStringCodingException If this value includes invalid UTF-8 byte sequence.
+ */
+ String asString();
- public RawValue toValue();
+ /**
+ * Returns the value as {@code String}.
+ *
+ * This method replaces an invalid UTF-8 byte sequence with U+FFFD replacement character.
+ */
+ String toString();
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/StringValue.java b/msgpack-core/src/main/java/org/msgpack/value/StringValue.java
index 35315f14d..0c812d58d 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/StringValue.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/StringValue.java
@@ -1,8 +1,30 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
/**
- * Created on 5/30/14.
+ * The interface {@code StringValue} represents MessagePack's String type.
+ *
+ * MessagePack's String type can represent a UTF-8 string at most 264 -1 bytes.
+ *
+ * Note that the value could include invalid byte sequences. {@code asString()} method throws {@code MessageTypeStringCodingException} if the value includes invalid byte sequence. {@code toJson()} method replaces an invalid byte sequence with U+FFFD replacement character.
+ *
+ * @see org.msgpack.value.RawValue
*/
-public interface StringValue extends RawValue {
- public StringValue toValue();
+public interface StringValue
+ extends RawValue
+{
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/Value.java b/msgpack-core/src/main/java/org/msgpack/value/Value.java
index 0cf02453c..2fae838b4 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/Value.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/Value.java
@@ -15,15 +15,248 @@
//
package org.msgpack.value;
+import org.msgpack.core.MessagePacker;
-import org.msgpack.core.*;
+import java.io.IOException;
/**
- * Value is a holder of a message-packed value.
+ * Value is an implementation of MessagePack type system. To retrieve values from a Value object,
+ * You need to check its {@link ValueType} then call an appropriate asXXXValue method.
+ *
+ *
+ *
*/
-public interface Value extends ValueRef {
+public interface Value
+{
+ /**
+ * Returns type of this value.
+ *
+ * Note that you can't use instanceof to check type of a value because type of a mutable value is variable.
+ */
+ ValueType getValueType();
- public ArrayValue asArrayValue() throws MessageTypeException;
- public MapValue asMapValue() throws MessageTypeException;
+ /**
+ * Returns immutable copy of this value.
+ *
+ * This method simply returns this without copying the value if this value is already immutable.
+ */
+ ImmutableValue immutableValue();
+ /**
+ * Returns true if type of this value is Nil.
+ *
+ * If this method returns true, {@code asNilValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((NilValue) thisValue) to check type of a value because type of a mutable value is variable.
+ */
+ boolean isNilValue();
+
+ /**
+ * Returns true if type of this value is Boolean.
+ *
+ * If this method returns true, {@code asBooleanValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((BooleanValue) thisValue) to check type of a value because type of a mutable value is variable.
+ */
+ boolean isBooleanValue();
+
+ /**
+ * Returns true if type of this value is Integer or Float.
+ *
+ * If this method returns true, {@code asNumberValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((NumberValue) thisValue) to check type of a value because type of a mutable value is variable.
+ */
+ boolean isNumberValue();
+
+ /**
+ * Returns true if type of this value is Integer.
+ *
+ * If this method returns true, {@code asIntegerValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((IntegerValue) thisValue) to check type of a value because type of a mutable value is variable.
+ */
+ boolean isIntegerValue();
+
+ /**
+ * Returns true if type of this value is Float.
+ *
+ * If this method returns true, {@code asFloatValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((FloatValue) thisValue) to check type of a value because type of a mutable value is variable.
+ */
+ boolean isFloatValue();
+
+ /**
+ * Returns true if type of this value is String or Binary.
+ *
+ * If this method returns true, {@code asRawValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((RawValue) thisValue) to check type of a value because type of a mutable value is variable.
+ */
+ boolean isRawValue();
+
+ /**
+ * Returns true if type of this value is Binary.
+ *
+ * If this method returns true, {@code asBinaryValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((BinaryValue) thisValue) to check type of a value because type of a mutable value is variable.
+ */
+ boolean isBinaryValue();
+
+ /**
+ * Returns true if type of this value is String.
+ *
+ * If this method returns true, {@code asStringValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((StringValue) thisValue) to check type of a value because type of a mutable value is variable.
+ */
+ boolean isStringValue();
+
+ /**
+ * Returns true if type of this value is Array.
+ *
+ * If this method returns true, {@code asArrayValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((ArrayValue) thisValue) to check type of a value because type of a mutable value is variable.
+ */
+ boolean isArrayValue();
+
+ /**
+ * Returns true if type of this value is Map.
+ *
+ * If this method returns true, {@code asMapValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((MapValue) thisValue) to check type of a value because type of a mutable value is variable.
+ */
+ boolean isMapValue();
+
+ /**
+ * Returns true if type of this an Extension.
+ *
+ * If this method returns true, {@code asExtensionValue} never throws exceptions.
+ * Note that you can't use instanceof or cast ((ExtensionValue) thisValue) to check type of a value because
+ * type of a mutable value is variable.
+ */
+ boolean isExtensionValue();
+
+ /**
+ * Returns the value as {@code NilValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((NilValue) thisValue) to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not Nil.
+ */
+ NilValue asNilValue();
+
+ /**
+ * Returns the value as {@code BooleanValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((BooleanValue) thisValue) to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not Boolean.
+ */
+ BooleanValue asBooleanValue();
+
+ /**
+ * Returns the value as {@code NumberValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((NumberValue) thisValue) to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not Integer or Float.
+ */
+ NumberValue asNumberValue();
+
+ /**
+ * Returns the value as {@code IntegerValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((IntegerValue) thisValue) to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not Integer.
+ */
+ IntegerValue asIntegerValue();
+
+ /**
+ * Returns the value as {@code FloatValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((FloatValue) thisValue) to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not Float.
+ */
+ FloatValue asFloatValue();
+
+ /**
+ * Returns the value as {@code RawValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((RawValue) thisValue) to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not Binary or String.
+ */
+ RawValue asRawValue();
+
+ /**
+ * Returns the value as {@code BinaryValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((BinaryValue) thisValue) to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not Binary.
+ */
+ BinaryValue asBinaryValue();
+
+ /**
+ * Returns the value as {@code StringValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((StringValue) thisValue) to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not String.
+ */
+ StringValue asStringValue();
+
+ /**
+ * Returns the value as {@code ArrayValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((ArrayValue) thisValue) to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not Array.
+ */
+ ArrayValue asArrayValue();
+
+ /**
+ * Returns the value as {@code MapValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((MapValue) thisValue) to check type of a value because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not Map.
+ */
+ MapValue asMapValue();
+
+ /**
+ * Returns the value as {@code ExtensionValue}. Otherwise throws {@code MessageTypeCastException}.
+ *
+ * Note that you can't use instanceof or cast ((ExtensionValue) thisValue) to check type of a value
+ * because type of a mutable value is variable.
+ *
+ * @throws MessageTypeCastException If type of this value is not an Extension.
+ */
+ ExtensionValue asExtensionValue();
+
+ /**
+ * Serializes the value using the specified {@code MessagePacker}
+ *
+ * @see MessagePacker
+ */
+ void writeTo(MessagePacker pk)
+ throws IOException;
+
+ /**
+ * Compares this value to the specified object.
+ *
+ * This method returns {@code true} if type and value are equivalent.
+ * If this value is {@code MapValue} or {@code ArrayValue}, this method check equivalence of elements recursively.
+ */
+ boolean equals(Object obj);
+
+ /**
+ * Returns json representation of this Value.
+ *
+ * Following behavior is not configurable at this release and they might be changed at future releases:
+ *
+ * * if a key of MapValue is not string, the key is converted to a string using toString method.
+ * * NaN and Infinity of DoubleValue are converted to null.
+ * * ExtensionValue is converted to a 2-element array where first element is a number and second element is the data encoded in hex.
+ * * BinaryValue is converted to a string using UTF-8 encoding. Invalid byte sequence is replaced with U+FFFD replacement character.
+ * * Invalid UTF-8 byte sequences in StringValue is replaced with U+FFFD replacement character
+ */
+ String toJson();
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ValueFactory.java b/msgpack-core/src/main/java/org/msgpack/value/ValueFactory.java
index d1b1a5c61..b0ffc932a 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/ValueFactory.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/ValueFactory.java
@@ -15,137 +15,213 @@
//
package org.msgpack.value;
-import org.msgpack.value.holder.FloatHolder;
-import org.msgpack.value.holder.IntegerHolder;
-import org.msgpack.value.impl.*;
+import org.msgpack.value.impl.ImmutableArrayValueImpl;
+import org.msgpack.value.impl.ImmutableBigIntegerValueImpl;
+import org.msgpack.value.impl.ImmutableBinaryValueImpl;
+import org.msgpack.value.impl.ImmutableBooleanValueImpl;
+import org.msgpack.value.impl.ImmutableDoubleValueImpl;
+import org.msgpack.value.impl.ImmutableExtensionValueImpl;
+import org.msgpack.value.impl.ImmutableLongValueImpl;
+import org.msgpack.value.impl.ImmutableMapValueImpl;
+import org.msgpack.value.impl.ImmutableNilValueImpl;
+import org.msgpack.value.impl.ImmutableStringValueImpl;
+import java.math.BigInteger;
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-/**
- * Factory for creting Value instances
- */
-public class ValueFactory {
- public static NilValue nilValue() {
- return NilValueImpl.getInstance();
+public final class ValueFactory
+{
+ private ValueFactory()
+ {
}
- public static BooleanValue newBoolean(boolean v) {
- return v ? BooleanValueImpl.TRUE : BooleanValueImpl.FALSE;
+ public static ImmutableNilValue newNil()
+ {
+ return ImmutableNilValueImpl.get();
}
- public static IntegerValue newByte(byte v) {
- return new IntegerValueImpl((int) v);
+ public static ImmutableBooleanValue newBoolean(boolean v)
+ {
+ return v ? ImmutableBooleanValueImpl.TRUE : ImmutableBooleanValueImpl.FALSE;
}
- public static IntegerValue newShort(short v) {
- return new IntegerValueImpl((int) v);
+ public static ImmutableIntegerValue newInteger(byte v)
+ {
+ return new ImmutableLongValueImpl(v);
}
- public static IntegerValue newInt(int v) {
- return new IntegerValueImpl(v);
+ public static ImmutableIntegerValue newInteger(short v)
+ {
+ return new ImmutableLongValueImpl(v);
}
- public static IntegerValue newLong(long v) {
- return new LongValueImpl(v);
+ public static ImmutableIntegerValue newInteger(int v)
+ {
+ return new ImmutableLongValueImpl(v);
}
- public static IntegerValue newBigInteger(BigInteger v) {
- return new BigIntegerValueImpl(v);
+ public static ImmutableIntegerValue newInteger(long v)
+ {
+ return new ImmutableLongValueImpl(v);
}
- public static FloatValue newFloat(float v) {
- return new FloatValueImpl(v);
+ public static ImmutableIntegerValue newInteger(BigInteger v)
+ {
+ return new ImmutableBigIntegerValueImpl(v);
}
- public static FloatValue newDouble(double v) {
- return new DoubleValueImpl(v);
+ public static ImmutableFloatValue newFloat(float v)
+ {
+ return new ImmutableDoubleValueImpl(v);
}
- public static BinaryValue newBinary(byte[] b) {
- return new BinaryValueImpl(ByteBuffer.wrap(b));
+ public static ImmutableFloatValue newFloat(double v)
+ {
+ return new ImmutableDoubleValueImpl(v);
}
- public static BinaryValue newBinary(byte[] b, int off, int len) {
- return new BinaryValueImpl(ByteBuffer.wrap(b, off, len));
+ public static ImmutableBinaryValue newBinary(byte[] b)
+ {
+ return new ImmutableBinaryValueImpl(b);
}
- public static BinaryValue newBinary(ByteBuffer bb) {
- return new BinaryValueImpl(bb.duplicate());
+ public static ImmutableBinaryValue newBinary(byte[] b, int off, int len)
+ {
+ return new ImmutableBinaryValueImpl(Arrays.copyOfRange(b, off, len));
}
- public static StringValue newString(String s) {
- return new StringValueImpl(s);
+ public static ImmutableStringValue newString(String s)
+ {
+ return new ImmutableStringValueImpl(s);
}
- public static StringValue newRawString(byte[] b) {
- return new RawStringValueImpl(ByteBuffer.wrap(b));
+ public static ImmutableStringValue newString(byte[] b)
+ {
+ return new ImmutableStringValueImpl(b);
}
- public static StringValue newRawString(byte[] b, int off, int len) {
- return new RawStringValueImpl(ByteBuffer.wrap(b, off, len));
+ public static ImmutableStringValue newString(byte[] b, int off, int len)
+ {
+ return new ImmutableStringValueImpl(Arrays.copyOfRange(b, off, len));
}
- public static StringValue newRawString(ByteBuffer bb) {
- return new RawStringValueImpl(bb.duplicate());
- }
-
- public static ArrayValue newArrayFrom(List extends Value> list) {
+ public static ImmutableArrayValue newArray(List extends Value> list)
+ {
if (list.isEmpty()) {
- return ArrayValueImpl.empty();
+ return ImmutableArrayValueImpl.empty();
}
Value[] array = list.toArray(new Value[list.size()]);
- return new ArrayValueImpl(array);
+ return new ImmutableArrayValueImpl(array);
}
- public static ArrayValue newArray(Value... array) {
+ public static ImmutableArrayValue newArray(Value... array)
+ {
if (array.length == 0) {
- return ArrayValueImpl.empty();
+ return ImmutableArrayValueImpl.empty();
}
- return new ArrayValueImpl(array);
+ return new ImmutableArrayValueImpl(Arrays.copyOf(array, array.length));
}
- public static ArrayValue emptyArray() {
- return ArrayValueImpl.empty();
+ public static ImmutableArrayValue emptyArray()
+ {
+ return ImmutableArrayValueImpl.empty();
}
- public static MapValue newMap(Map map) {
- Value[] keyValueSequence = new Value[map.size() * 2];
+ public static
+ ImmutableMapValue newMap(Map map)
+ {
+ Value[] kvs = new Value[map.size() * 2];
Iterator> ite = map.entrySet().iterator();
int index = 0;
while (ite.hasNext()) {
Map.Entry pair = ite.next();
- keyValueSequence[index++] = pair.getKey();
- keyValueSequence[index++] = pair.getValue();
+ kvs[index] = pair.getKey();
+ index++;
+ kvs[index] = pair.getValue();
+ index++;
+ }
+ return newMap(kvs);
+ }
+
+ public static ImmutableMapValue newMap(Value[] kvs)
+ {
+ if (kvs.length == 0) {
+ return ImmutableMapValueImpl.empty();
}
- return newMap(keyValueSequence);
+ return new ImmutableMapValueImpl(Arrays.copyOf(kvs, kvs.length));
}
- public static MapValue newMap(Value[] keyValueSequence) {
- if (keyValueSequence.length == 0) {
- return MapValueImpl.empty();
+ public static ImmutableMapValue emptyMap()
+ {
+ return ImmutableMapValueImpl.empty();
+ }
+
+ public static MapValue newMap(Map.Entry extends Value, ? extends Value>... pairs)
+ {
+ MapBuilder b = new MapBuilder();
+ for (Map.Entry extends Value, ? extends Value> p : pairs) {
+ b.put(p);
}
- return new MapValueImpl(keyValueSequence);
+ return b.build();
}
- public static MapValue emptyMap() {
- return MapValueImpl.empty();
+ public static MapBuilder newMapBuilder()
+ {
+ return new MapBuilder();
}
- public static ExtendedValue newExtendedValue(int extType, byte[] extData) {
- return newExtendedValue(extType, ByteBuffer.wrap(extData));
+ public static Map.Entry newMapEntry(Value key, Value value)
+ {
+ return new AbstractMap.SimpleEntry(key, value);
}
- public static ExtendedValue newExtendedValue(int extType, ByteBuffer extData) {
- return new ExtendedValueImpl(extType, extData);
+ public static class MapBuilder
+ {
+ private final Map map = new HashMap();
+
+ public MapBuilder() {}
+
+ public MapValue build()
+ {
+ return newMap(map);
+ }
+
+ public MapBuilder put(Map.Entry extends Value, ? extends Value> pair)
+ {
+ put(pair.getKey(), pair.getValue());
+ return this;
+ }
+
+ public MapBuilder put(Value key, Value value)
+ {
+ map.put(key, value);
+ return this;
+ }
+
+ public MapBuilder putAll(Iterable extends Map.Entry extends Value, ? extends Value>> entries)
+ {
+ for (Map.Entry extends Value, ? extends Value> entry : entries) {
+ put(entry.getKey(), entry.getValue());
+ }
+ return this;
+ }
+
+ public MapBuilder putAll(Map extends Value, ? extends Value> map)
+ {
+ for (Map.Entry extends Value, ? extends Value> entry : map.entrySet()) {
+ put(entry);
+ }
+ return this;
+ }
}
- /**
- * Hide the default constructor to forbid instantiation of this class
- */
- protected ValueFactory() {
+ public static ImmutableExtensionValue newExtension(byte type, byte[] data)
+ {
+ return new ImmutableExtensionValueImpl(type, data);
}
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ValueRef.java b/msgpack-core/src/main/java/org/msgpack/value/ValueRef.java
deleted file mode 100644
index 0043e4392..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/ValueRef.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.msgpack.value;
-
-import org.msgpack.core.MessagePacker;
-import org.msgpack.core.MessageTypeException;
-
-import java.io.IOException;
-
-/**
- * Reference to the value
- */
-public interface ValueRef {
- public ValueType getValueType();
-
- public NilValue asNil() throws MessageTypeException;
- public BooleanValue asBoolean() throws MessageTypeException;
- public NumberValue asNumber() throws MessageTypeException;
- public IntegerValue asInteger() throws MessageTypeException;
- public FloatValue asFloat() throws MessageTypeException;
- public BinaryValue asBinary() throws MessageTypeException;
- public StringValue asString() throws MessageTypeException;
- public RawValue asRaw() throws MessageTypeException;
- public ExtendedValue asExtended() throws MessageTypeException;
-
- public ArrayCursor getArrayCursor() throws MessageTypeException;
- public MapCursor getMapCursor() throws MessageTypeException;
-
- public boolean isNil();
- public boolean isBoolean();
- public boolean isNumber();
- public boolean isInteger();
- public boolean isFloat();
- public boolean isBinary();
- public boolean isString();
- public boolean isRaw();
- public boolean isArray();
- public boolean isMap();
- public boolean isExtended();
-
- public void writeTo(MessagePacker packer) throws IOException;
-
- public void accept(ValueVisitor visitor);
-
- /**
- * Create an immutable value from this reference
- * @return
- */
- public Value toValue();
-
- /**
- * Test whether this value is a reference of not.
- * @return true if this value is reference, otherwise false.
- */
- public boolean isRef();
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/ValueType.java b/msgpack-core/src/main/java/org/msgpack/value/ValueType.java
index 5e1b27b99..715ebbd18 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/ValueType.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/ValueType.java
@@ -15,13 +15,11 @@
//
package org.msgpack.value;
-import org.msgpack.core.MessageFormat;
-
/**
* MessageTypeFamily is a group of {@link org.msgpack.core.MessageFormat}s
*/
-public enum ValueType {
-
+public enum ValueType
+{
NIL(false, false),
BOOLEAN(false, false),
INTEGER(true, false),
@@ -30,77 +28,69 @@ public enum ValueType {
BINARY(false, true),
ARRAY(false, false),
MAP(false, false),
- EXTENDED(false, true);
-
+ EXTENSION(false, true);
private final boolean numberType;
private final boolean rawType;
- private final int bitMask;
- private ValueType(boolean numberType, boolean rawType) {
+ private ValueType(boolean numberType, boolean rawType)
+ {
this.numberType = numberType;
this.rawType = rawType;
- this.bitMask = 1 << this.ordinal();
- }
-
- public int getBitMask() {
- return bitMask;
- }
-
- public boolean isTypeOf(int bitMask) {
- return (this.bitMask & bitMask) != 0;
}
- public boolean isNilType() {
+ public boolean isNilType()
+ {
return this == NIL;
}
- public boolean isBooleanType() {
+ public boolean isBooleanType()
+ {
return this == BOOLEAN;
}
- public boolean isNumberType() {
+ public boolean isNumberType()
+ {
return numberType;
}
- public boolean isIntegerType() {
+ public boolean isIntegerType()
+ {
return this == INTEGER;
}
- public boolean isFloatType() {
+ public boolean isFloatType()
+ {
return this == FLOAT;
}
- public boolean isRawType() {
+ public boolean isRawType()
+ {
return rawType;
}
- public boolean isStringType() {
+ public boolean isStringType()
+ {
return this == STRING;
}
- public boolean isBinaryType() {
+ public boolean isBinaryType()
+ {
return this == BINARY;
}
- public boolean isArrayType() {
+ public boolean isArrayType()
+ {
return this == ARRAY;
}
- public boolean isMapType() {
+ public boolean isMapType()
+ {
return this == MAP;
}
- public boolean isExtendedType() {
- return this == EXTENDED;
- }
-
- public static ValueType valueOf(byte b) {
- return MessageFormat.valueOf(b).getValueType();
+ public boolean isExtensionType()
+ {
+ return this == EXTENSION;
}
-
- public String toTypeName() {
- return this.name().substring(0, 1) + this.name().substring(1).toLowerCase();
- }
-
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/Variable.java b/msgpack-core/src/main/java/org/msgpack/value/Variable.java
new file mode 100644
index 000000000..59e6930cb
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/Variable.java
@@ -0,0 +1,1235 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value;
+
+import org.msgpack.core.MessageFormat;
+import org.msgpack.core.MessageIntegerOverflowException;
+import org.msgpack.core.MessagePack;
+import org.msgpack.core.MessagePacker;
+import org.msgpack.core.MessageStringCodingException;
+import org.msgpack.core.MessageTypeCastException;
+import org.msgpack.value.impl.ImmutableBigIntegerValueImpl;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class Variable
+ implements Value
+{
+ private abstract class AbstractValueAccessor
+ implements Value
+ {
+ @Override
+ public boolean isNilValue()
+ {
+ return getValueType().isNilType();
+ }
+
+ @Override
+ public boolean isBooleanValue()
+ {
+ return getValueType().isBooleanType();
+ }
+
+ @Override
+ public boolean isNumberValue()
+ {
+ return getValueType().isNumberType();
+ }
+
+ @Override
+ public boolean isIntegerValue()
+ {
+ return getValueType().isIntegerType();
+ }
+
+ @Override
+ public boolean isFloatValue()
+ {
+ return getValueType().isFloatType();
+ }
+
+ @Override
+ public boolean isRawValue()
+ {
+ return getValueType().isRawType();
+ }
+
+ @Override
+ public boolean isBinaryValue()
+ {
+ return getValueType().isBinaryType();
+ }
+
+ @Override
+ public boolean isStringValue()
+ {
+ return getValueType().isStringType();
+ }
+
+ @Override
+ public boolean isArrayValue()
+ {
+ return getValueType().isArrayType();
+ }
+
+ @Override
+ public boolean isMapValue()
+ {
+ return getValueType().isMapType();
+ }
+
+ @Override
+ public boolean isExtensionValue()
+ {
+ return getValueType().isExtensionType();
+ }
+
+ @Override
+ public NilValue asNilValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public BooleanValue asBooleanValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public NumberValue asNumberValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public IntegerValue asIntegerValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public FloatValue asFloatValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public RawValue asRawValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public BinaryValue asBinaryValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public StringValue asStringValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ArrayValue asArrayValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public MapValue asMapValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ExtensionValue asExtensionValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return Variable.this.equals(obj);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Variable.this.hashCode();
+ }
+
+ @Override
+ public String toJson()
+ {
+ return Variable.this.toJson();
+ }
+
+ @Override
+ public String toString()
+ {
+ return Variable.this.toString();
+ }
+ }
+
+ public static enum Type
+ {
+ NULL(ValueType.NIL),
+ BOOLEAN(ValueType.BOOLEAN),
+ LONG(ValueType.INTEGER),
+ BIG_INTEGER(ValueType.INTEGER),
+ DOUBLE(ValueType.FLOAT),
+ BYTE_ARRAY(ValueType.BINARY),
+ RAW_STRING(ValueType.STRING),
+ LIST(ValueType.ARRAY),
+ MAP(ValueType.MAP),
+ EXTENSION(ValueType.EXTENSION);
+
+ private final ValueType valueType;
+
+ private Type(ValueType valueType)
+ {
+ this.valueType = valueType;
+ }
+
+ public ValueType getValueType()
+ {
+ return valueType;
+ }
+ }
+
+ private final NilValueAccessor nilAccessor = new NilValueAccessor();
+ private final BooleanValueAccessor booleanAccessor = new BooleanValueAccessor();
+ private final IntegerValueAccessor integerAccessor = new IntegerValueAccessor();
+ private final FloatValueAccessor floatAccessor = new FloatValueAccessor();
+ private final BinaryValueAccessor binaryAccessor = new BinaryValueAccessor();
+ private final StringValueAccessor stringAccessor = new StringValueAccessor();
+ private final ArrayValueAccessor arrayAccessor = new ArrayValueAccessor();
+ private final MapValueAccessor mapAccessor = new MapValueAccessor();
+ private final ExtensionValueAccessor extensionAccessor = new ExtensionValueAccessor();
+
+ private Type type;
+
+ private long longValue;
+ private double doubleValue;
+ private Object objectValue;
+
+ private AbstractValueAccessor accessor;
+
+ public Variable()
+ {
+ setNilValue();
+ }
+
+ ////
+ // NilValue
+ //
+
+ public Variable setNilValue()
+ {
+ this.type = Type.NULL;
+ this.accessor = nilAccessor;
+ return this;
+ }
+
+ private class NilValueAccessor
+ extends AbstractValueAccessor
+ implements NilValue
+ {
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.NIL;
+ }
+
+ @Override
+ public NilValue asNilValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableNilValue immutableValue()
+ {
+ return ValueFactory.newNil();
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ pk.packNil();
+ }
+ }
+
+ ////
+ // BooleanValue
+ //
+
+ public Variable setBooleanValue(boolean v)
+ {
+ this.type = Type.BOOLEAN;
+ this.accessor = booleanAccessor;
+ this.longValue = (v ? 1L : 0L);
+ return this;
+ }
+
+ private class BooleanValueAccessor
+ extends AbstractValueAccessor
+ implements BooleanValue
+ {
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.BOOLEAN;
+ }
+
+ @Override
+ public BooleanValue asBooleanValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableBooleanValue immutableValue()
+ {
+ return ValueFactory.newBoolean(getBoolean());
+ }
+
+ @Override
+ public boolean getBoolean()
+ {
+ return longValue == 1L;
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ pk.packBoolean(longValue == 1L);
+ }
+ }
+
+ ////
+ // NumberValue
+ // IntegerValue
+ // FloatValue
+ //
+
+ private static final BigInteger LONG_MIN = BigInteger.valueOf((long) Long.MIN_VALUE);
+ private static final BigInteger LONG_MAX = BigInteger.valueOf((long) Long.MAX_VALUE);
+ private static final long BYTE_MIN = (long) Byte.MIN_VALUE;
+ private static final long BYTE_MAX = (long) Byte.MAX_VALUE;
+ private static final long SHORT_MIN = (long) Short.MIN_VALUE;
+ private static final long SHORT_MAX = (long) Short.MAX_VALUE;
+ private static final long INT_MIN = (long) Integer.MIN_VALUE;
+ private static final long INT_MAX = (long) Integer.MAX_VALUE;
+
+ private abstract class AbstractNumberValueAccessor
+ extends AbstractValueAccessor
+ implements NumberValue
+ {
+ @Override
+ public NumberValue asNumberValue()
+ {
+ return this;
+ }
+
+ @Override
+ public byte toByte()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return ((BigInteger) objectValue).byteValue();
+ }
+ return (byte) longValue;
+ }
+
+ @Override
+ public short toShort()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return ((BigInteger) objectValue).shortValue();
+ }
+ return (short) longValue;
+ }
+
+ @Override
+ public int toInt()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return ((BigInteger) objectValue).intValue();
+ }
+ return (int) longValue;
+ }
+
+ @Override
+ public long toLong()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return ((BigInteger) objectValue).longValue();
+ }
+ return (long) longValue;
+ }
+
+ @Override
+ public BigInteger toBigInteger()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return (BigInteger) objectValue;
+ }
+ else if (type == Type.DOUBLE) {
+ return new BigDecimal(doubleValue).toBigInteger();
+ }
+ return BigInteger.valueOf(longValue);
+ }
+
+ @Override
+ public float toFloat()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return ((BigInteger) objectValue).floatValue();
+ }
+ else if (type == Type.DOUBLE) {
+ return (float) doubleValue;
+ }
+ return (float) longValue;
+ }
+
+ @Override
+ public double toDouble()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return ((BigInteger) objectValue).doubleValue();
+ }
+ else if (type == Type.DOUBLE) {
+ return doubleValue;
+ }
+ return (double) longValue;
+ }
+ }
+
+ ////
+ // IntegerValue
+ //
+
+ public Variable setIntegerValue(long v)
+ {
+ this.type = Type.LONG;
+ this.accessor = integerAccessor;
+ this.longValue = v;
+ return this;
+ }
+
+ public Variable setIntegerValue(BigInteger v)
+ {
+ if (0 <= v.compareTo(LONG_MIN) && v.compareTo(LONG_MAX) <= 0) {
+ this.type = Type.LONG;
+ this.accessor = integerAccessor;
+ this.longValue = v.longValue();
+ }
+ else {
+ this.type = Type.BIG_INTEGER;
+ this.accessor = integerAccessor;
+ this.objectValue = v;
+ }
+ return this;
+ }
+
+ private class IntegerValueAccessor
+ extends AbstractNumberValueAccessor
+ implements IntegerValue
+ {
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.INTEGER;
+ }
+
+ @Override
+ public IntegerValue asIntegerValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableIntegerValue immutableValue()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return ValueFactory.newInteger((BigInteger) objectValue);
+ }
+ return ValueFactory.newInteger(longValue);
+ }
+
+ @Override
+ public boolean isInByteRange()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return false;
+ }
+ return BYTE_MIN <= longValue && longValue <= BYTE_MAX;
+ }
+
+ @Override
+ public boolean isInShortRange()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return false;
+ }
+ return SHORT_MIN <= longValue && longValue <= SHORT_MAX;
+ }
+
+ @Override
+ public boolean isInIntRange()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return false;
+ }
+ return INT_MIN <= longValue && longValue <= INT_MAX;
+ }
+
+ @Override
+ public boolean isInLongRange()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public MessageFormat mostSuccinctMessageFormat()
+ {
+ return ImmutableBigIntegerValueImpl.mostSuccinctMessageFormat(this);
+ }
+
+ @Override
+ public byte asByte()
+ {
+ if (!isInByteRange()) {
+ throw new MessageIntegerOverflowException(longValue);
+ }
+ return (byte) longValue;
+ }
+
+ @Override
+ public short asShort()
+ {
+ if (!isInByteRange()) {
+ throw new MessageIntegerOverflowException(longValue);
+ }
+ return (short) longValue;
+ }
+
+ @Override
+ public int asInt()
+ {
+ if (!isInIntRange()) {
+ throw new MessageIntegerOverflowException(longValue);
+ }
+ return (int) longValue;
+ }
+
+ @Override
+ public long asLong()
+ {
+ if (!isInLongRange()) {
+ throw new MessageIntegerOverflowException(longValue);
+ }
+ return longValue;
+ }
+
+ @Override
+ public BigInteger asBigInteger()
+ {
+ if (type == Type.BIG_INTEGER) {
+ return (BigInteger) objectValue;
+ }
+ else {
+ return BigInteger.valueOf(longValue);
+ }
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ if (type == Type.BIG_INTEGER) {
+ pk.packBigInteger((BigInteger) objectValue);
+ }
+ else {
+ pk.packLong(longValue);
+ }
+ }
+ }
+
+ ////
+ // FloatValue
+ //
+
+ public Variable setFloatValue(double v)
+ {
+ this.type = Type.DOUBLE;
+ this.accessor = floatAccessor;
+ this.doubleValue = v;
+ this.longValue = (long) v; // AbstractNumberValueAccessor uses toLong
+ return this;
+ }
+
+ public Variable setFloatValue(float v)
+ {
+ this.type = Type.DOUBLE;
+ this.accessor = floatAccessor;
+ this.longValue = (long) v; // AbstractNumberValueAccessor uses toLong
+ return this;
+ }
+
+ private class FloatValueAccessor
+ extends AbstractNumberValueAccessor
+ implements FloatValue
+ {
+ @Override
+ public FloatValue asFloatValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableFloatValue immutableValue()
+ {
+ return ValueFactory.newFloat(doubleValue);
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.FLOAT;
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ pk.packDouble(doubleValue);
+ }
+ }
+
+ ////
+ // RawValue
+ // BinaryValue
+ // StringValue
+ //
+
+ private abstract class AbstractRawValueAccessor
+ extends AbstractValueAccessor
+ implements RawValue
+ {
+ @Override
+ public RawValue asRawValue()
+ {
+ return this;
+ }
+
+ @Override
+ public byte[] asByteArray()
+ {
+ return (byte[]) objectValue;
+ }
+
+ @Override
+ public ByteBuffer asByteBuffer()
+ {
+ return ByteBuffer.wrap(asByteArray());
+ }
+
+ @Override
+ public String asString()
+ {
+ byte[] raw = (byte[]) objectValue;
+ try {
+ CharsetDecoder reportDecoder = MessagePack.UTF8.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ return reportDecoder.decode(ByteBuffer.wrap(raw)).toString();
+ }
+ catch (CharacterCodingException ex) {
+ throw new MessageStringCodingException(ex);
+ }
+ }
+
+ // override for performance optimization
+ @Override
+ public String toString()
+ {
+ byte[] raw = (byte[]) objectValue;
+ try {
+ CharsetDecoder reportDecoder = MessagePack.UTF8.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE);
+ return reportDecoder.decode(ByteBuffer.wrap(raw)).toString();
+ }
+ catch (CharacterCodingException ex) {
+ throw new MessageStringCodingException(ex);
+ }
+ }
+ }
+
+ ////
+ // BinaryValue
+ //
+
+ public Variable setBinaryValue(byte[] v)
+ {
+ this.type = Type.BYTE_ARRAY;
+ this.accessor = binaryAccessor;
+ this.objectValue = v;
+ return this;
+ }
+
+ private class BinaryValueAccessor
+ extends AbstractRawValueAccessor
+ implements BinaryValue
+ {
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.BINARY;
+ }
+
+ @Override
+ public BinaryValue asBinaryValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableBinaryValue immutableValue()
+ {
+ return ValueFactory.newBinary(asByteArray());
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ byte[] data = (byte[]) objectValue;
+ pk.packBinaryHeader(data.length);
+ pk.writePayload(data);
+ }
+ }
+
+ ////
+ // StringValue
+ //
+
+ public Variable setStringValue(String v)
+ {
+ return setStringValue(v.getBytes(MessagePack.UTF8));
+ }
+
+ public Variable setStringValue(byte[] v)
+ {
+ this.type = Type.RAW_STRING;
+ this.accessor = stringAccessor;
+ this.objectValue = v;
+ return this;
+ }
+
+ private class StringValueAccessor
+ extends AbstractRawValueAccessor
+ implements StringValue
+ {
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.STRING;
+ }
+
+ @Override
+ public StringValue asStringValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableStringValue immutableValue()
+ {
+ return ValueFactory.newString((byte[]) objectValue);
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ byte[] data = (byte[]) objectValue;
+ pk.packRawStringHeader(data.length);
+ pk.writePayload(data);
+ }
+ }
+
+ ////
+ // ArrayValue
+ //
+
+ public Variable setArrayValue(List v)
+ {
+ this.type = Type.LIST;
+ this.accessor = arrayAccessor;
+ this.objectValue = v;
+ return this;
+ }
+
+ private class ArrayValueAccessor
+ extends AbstractValueAccessor
+ implements ArrayValue
+ {
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.ARRAY;
+ }
+
+ @Override
+ public ArrayValue asArrayValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableArrayValue immutableValue()
+ {
+ return ValueFactory.newArray(list());
+ }
+
+ @Override
+ public int size()
+ {
+ return list().size();
+ }
+
+ @Override
+ public Value get(int index)
+ {
+ return list().get(index);
+ }
+
+ @Override
+ public Value getOrNilValue(int index)
+ {
+ List l = list();
+ if (l.size() < index && index >= 0) {
+ return ValueFactory.newNil();
+ }
+ return l.get(index);
+ }
+
+ @Override
+ public Iterator iterator()
+ {
+ return list().iterator();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public List list()
+ {
+ return (List) objectValue;
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ List l = list();
+ pk.packArrayHeader(l.size());
+ for (Value e : l) {
+ e.writeTo(pk);
+ }
+ }
+ }
+
+ ////
+ // MapValue
+ //
+
+ public Variable setMapValue(Map v)
+ {
+ this.type = Type.MAP;
+ this.accessor = mapAccessor;
+ this.objectValue = v;
+ return this;
+ }
+
+ private class MapValueAccessor
+ extends AbstractValueAccessor
+ implements MapValue
+ {
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.MAP;
+ }
+
+ @Override
+ public MapValue asMapValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableMapValue immutableValue()
+ {
+ return ValueFactory.newMap(map());
+ }
+
+ @Override
+ public int size()
+ {
+ return map().size();
+ }
+
+ @Override
+ public Set keySet()
+ {
+ return map().keySet();
+ }
+
+ @Override
+ public Set> entrySet()
+ {
+ return map().entrySet();
+ }
+
+ @Override
+ public Collection values()
+ {
+ return map().values();
+ }
+
+ @Override
+ public Value[] getKeyValueArray()
+ {
+ Map v = map();
+ Value[] kvs = new Value[v.size() * 2];
+ Iterator> ite = v.entrySet().iterator();
+ int i = 0;
+ while (ite.hasNext()) {
+ Map.Entry pair = ite.next();
+ kvs[i] = pair.getKey();
+ i++;
+ kvs[i] = pair.getValue();
+ i++;
+ }
+ return kvs;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map map()
+ {
+ return (Map) objectValue;
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ Map m = map();
+ pk.packArrayHeader(m.size());
+ for (Map.Entry pair : m.entrySet()) {
+ pair.getKey().writeTo(pk);
+ pair.getValue().writeTo(pk);
+ }
+ }
+ }
+
+ ////
+ // ExtensionValue
+ //
+ public Variable setExtensionValue(byte type, byte[] data)
+ {
+ this.type = Type.EXTENSION;
+ this.accessor = extensionAccessor;
+ this.objectValue = ValueFactory.newExtension(type, data);
+ return this;
+ }
+
+ private class ExtensionValueAccessor
+ extends AbstractValueAccessor
+ implements ExtensionValue
+ {
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.EXTENSION;
+ }
+
+ @Override
+ public ExtensionValue asExtensionValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableExtensionValue immutableValue()
+ {
+ return (ImmutableExtensionValue) objectValue;
+ }
+
+ @Override
+ public byte getType()
+ {
+ return ((ImmutableExtensionValue) objectValue).getType();
+ }
+
+ @Override
+ public byte[] getData()
+ {
+ return ((ImmutableExtensionValue) objectValue).getData();
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ ((ImmutableExtensionValue) objectValue).writeTo(pk);
+ }
+ }
+
+ ////
+ // Value
+ //
+
+ @Override
+ public ImmutableValue immutableValue()
+ {
+ return accessor.immutableValue();
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ accessor.writeTo(pk);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return immutableValue().hashCode(); // TODO optimize
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ return immutableValue().equals(o); // TODO optimize
+ }
+
+ @Override
+ public String toJson()
+ {
+ return immutableValue().toJson(); // TODO optimize
+ }
+
+ @Override
+ public String toString()
+ {
+ return immutableValue().toString(); // TODO optimize
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return type.getValueType();
+ }
+
+ @Override
+ public boolean isNilValue()
+ {
+ return getValueType().isNilType();
+ }
+
+ @Override
+ public boolean isBooleanValue()
+ {
+ return getValueType().isBooleanType();
+ }
+
+ @Override
+ public boolean isNumberValue()
+ {
+ return getValueType().isNumberType();
+ }
+
+ @Override
+ public boolean isIntegerValue()
+ {
+ return getValueType().isIntegerType();
+ }
+
+ @Override
+ public boolean isFloatValue()
+ {
+ return getValueType().isFloatType();
+ }
+
+ @Override
+ public boolean isRawValue()
+ {
+ return getValueType().isRawType();
+ }
+
+ @Override
+ public boolean isBinaryValue()
+ {
+ return getValueType().isBinaryType();
+ }
+
+ @Override
+ public boolean isStringValue()
+ {
+ return getValueType().isStringType();
+ }
+
+ @Override
+ public boolean isArrayValue()
+ {
+ return getValueType().isArrayType();
+ }
+
+ @Override
+ public boolean isMapValue()
+ {
+ return getValueType().isMapType();
+ }
+
+ @Override
+ public boolean isExtensionValue()
+ {
+ return getValueType().isExtensionType();
+ }
+
+ @Override
+ public NilValue asNilValue()
+ {
+ if (!isNilValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (NilValue) accessor;
+ }
+
+ @Override
+ public BooleanValue asBooleanValue()
+ {
+ if (!isBooleanValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (BooleanValue) accessor;
+ }
+
+ @Override
+ public NumberValue asNumberValue()
+ {
+ if (!isNumberValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (NumberValue) accessor;
+ }
+
+ @Override
+ public IntegerValue asIntegerValue()
+ {
+ if (!isIntegerValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (IntegerValue) accessor;
+ }
+
+ @Override
+ public FloatValue asFloatValue()
+ {
+ if (!isFloatValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (FloatValue) accessor;
+ }
+
+ @Override
+ public RawValue asRawValue()
+ {
+ if (!isRawValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (RawValue) accessor;
+ }
+
+ @Override
+ public BinaryValue asBinaryValue()
+ {
+ if (!isBinaryValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (BinaryValue) accessor;
+ }
+
+ @Override
+ public StringValue asStringValue()
+ {
+ if (!isStringValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (StringValue) accessor;
+ }
+
+ @Override
+ public ArrayValue asArrayValue()
+ {
+ if (!isArrayValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (ArrayValue) accessor;
+ }
+
+ @Override
+ public MapValue asMapValue()
+ {
+ if (!isMapValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (MapValue) accessor;
+ }
+
+ @Override
+ public ExtensionValue asExtensionValue()
+ {
+ if (!isExtensionValue()) {
+ throw new MessageTypeCastException();
+ }
+ return (ExtensionValue) accessor;
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/holder/ExtHolder.java b/msgpack-core/src/main/java/org/msgpack/value/holder/ExtHolder.java
deleted file mode 100644
index b5a8eaf58..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/holder/ExtHolder.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.msgpack.value.holder;
-
-import org.msgpack.core.MessagePack;
-import org.msgpack.core.MessagePacker;
-import org.msgpack.core.MessageStringCodingException;
-import org.msgpack.core.buffer.MessageBuffer;
-import org.msgpack.value.*;
-import org.msgpack.value.impl.AbstractValue;
-import org.msgpack.value.impl.AbstractValueRef;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Created on 6/13/14.
- */
-public class ExtHolder extends AbstractValueRef implements ExtendedValue {
-
- private int extType;
- private MessageBuffer buffer;
-
-
- public void setExtType(int extType, MessageBuffer buffer) {
- this.extType = extType;
- this.buffer = buffer;
- }
-
- @Override
- public int getExtType() {
- return extType;
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.EXTENDED;
- }
-
- @Override
- public void writeTo(MessagePacker packer) throws IOException {
- packer.packExtendedTypeHeader(extType, buffer.size()).writePayload(buffer.toByteBuffer());
- }
-
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitExtended(this);
- }
- @Override
- public ExtendedValue toValue() {
- // clone the buffer contents
- return ValueFactory.newExtendedValue(extType, buffer.toByteArray());
- }
-
- @Override
- public byte[] toByteArray() {
- return buffer.toByteArray();
- }
- @Override
- public ByteBuffer toByteBuffer() {
- return buffer.toByteBuffer();
- }
- @Override
- public MessageBuffer toMessageBuffer() {
- return buffer;
- }
-
- @Override
- public String toString() throws MessageStringCodingException {
- return new String(buffer.toByteArray(), MessagePack.UTF8);
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/holder/FloatHolder.java b/msgpack-core/src/main/java/org/msgpack/value/holder/FloatHolder.java
deleted file mode 100644
index a26e3efcc..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/holder/FloatHolder.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package org.msgpack.value.holder;
-
-import org.msgpack.core.MessageFloatOverflowException;
-import org.msgpack.core.MessageOverflowException;
-import org.msgpack.core.MessagePacker;
-import org.msgpack.value.*;
-import org.msgpack.value.impl.AbstractValue;
-import org.msgpack.value.impl.AbstractValueRef;
-
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-
-/**
- * Created on 6/3/14.
- */
-public class FloatHolder extends AbstractValueRef implements FloatValue {
-
- public static enum Type {
- FLOAT,
- DOUBLE
- }
-
- private Type tpe;
- private double value;
-
- @Override
- public boolean isValidByte() {
- return ((double) ((byte) value)) == value;
- }
- @Override
- public boolean isValidShort() {
- return ((double) ((short) value)) == value;
- }
- @Override
- public boolean isValidInt() {
- return ((double) ((int) value)) == value;
- }
- @Override
- public boolean isValidLong() {
- long l = (long) value;
- return ((double) l) == value && l != Long.MAX_VALUE;
- }
- @Override
- public boolean isWhole() {
- long l = (long) value;
- return ((double) l == value) || l == Long.MAX_VALUE && value < Double.POSITIVE_INFINITY || l == Long.MIN_VALUE && value > Double.NEGATIVE_INFINITY;
- }
- @Override
- public byte toByte() {
- return (byte) value;
- }
-
- @Override
- public short toShort() {
- return (short) value;
- }
-
- @Override
- public int toInt() {
- return (int) value;
- }
-
- @Override
- public long toLong() {
- return (long) value;
- }
-
- @Override
- public BigInteger toBigInteger() {
- return new BigDecimal(value).toBigInteger();
- }
-
- @Override
- public float toFloat() {
- return (float) value;
- }
-
- @Override
- public double toDouble() {
- return value;
- }
- @Override
- public byte asByte() throws MessageOverflowException {
- if(!isValidByte())
- throw new MessageFloatOverflowException(value);
- return (byte) value;
- }
- @Override
- public short asShort() throws MessageOverflowException {
- if(!isValidShort())
- throw new MessageFloatOverflowException(value);
- return (short) value;
- }
- @Override
- public int asInt() throws MessageOverflowException {
- if(!isValidInt())
- throw new MessageFloatOverflowException(value);
- return (int) value;
- }
- @Override
- public long asLong() throws MessageOverflowException {
- if(!isValidLong())
- throw new MessageFloatOverflowException(value);
- return (long) value;
- }
- @Override
- public BigInteger asBigInteger() throws MessageOverflowException {
- if(!isWhole())
- throw new MessageFloatOverflowException(value);
- return new BigDecimal(value).toBigInteger();
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.FLOAT;
- }
- @Override
- public void writeTo(MessagePacker pk) throws IOException {
- switch(tpe) {
- case FLOAT:
- pk.packFloat(toFloat());
- break;
- case DOUBLE:
- pk.packDouble(value);
- break;
- }
- }
-
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitFloat(this);
- }
- @Override
- public FloatValue toValue() {
- switch(tpe) {
- case FLOAT:
- return ValueFactory.newFloat(toFloat());
- case DOUBLE:
- return ValueFactory.newDouble(toDouble());
- default:
- throw new IllegalStateException("cannot reach here");
- }
- }
-
- public Type getType() {
- return tpe;
- }
-
- public void setFloat(float v) {
- tpe = Type.FLOAT;
- value = v;
- }
-
- public void setDouble(double v) {
- tpe = Type.DOUBLE;
- value = v;
- }
-
- @Override
- public int hashCode() {
- long v = Double.doubleToLongBits(value);
- return (int) (v ^ (v >>> 32));
- }
-
- @Override
- public String toString() {
- switch(tpe) {
- case FLOAT:
- return Float.toString((float) value);
- case DOUBLE:
- return Double.toString(value);
- default:
- throw new IllegalStateException("cannto reach here");
- }
- }
-
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/holder/IntegerHolder.java b/msgpack-core/src/main/java/org/msgpack/value/holder/IntegerHolder.java
deleted file mode 100644
index 6cea891bb..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/holder/IntegerHolder.java
+++ /dev/null
@@ -1,269 +0,0 @@
-package org.msgpack.value.holder;
-
-import org.msgpack.core.MessageIntegerOverflowException;
-import org.msgpack.core.MessagePacker;
-import org.msgpack.core.MessageTypeException;
-import org.msgpack.value.*;
-import org.msgpack.value.impl.AbstractValue;
-import org.msgpack.value.impl.AbstractValueRef;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import static org.msgpack.core.NumberUtil.*;
-
-/**
- * Union of integer values
- */
-public class IntegerHolder extends AbstractValueRef implements IntegerValue {
-
- @Override
- public ValueType getValueType() {
- return ValueType.INTEGER;
- }
- @Override
- public void writeTo(MessagePacker packer) throws IOException {
- switch(tpe) {
- case BIG_INTEGER:
- packer.packBigInteger(biValue);
- break;
- default:
- packer.packLong(longValue);
- break;
- }
- }
-
- @Override
- public IntegerValue asInteger() throws MessageTypeException {
- return this;
- }
-
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitInteger(this);
- }
-
- @Override
- public IntegerValue toValue() {
- switch(tpe){
- case BYTE:
- return ValueFactory.newByte(toByte());
- case SHORT:
- return ValueFactory.newShort(toShort());
- case INT:
- return ValueFactory.newInt(toInt());
- case LONG:
- return ValueFactory.newLong(toLong());
- case BIG_INTEGER:
- return ValueFactory.newBigInteger(toBigInteger());
- default:
- throw new IllegalStateException("cannot reach here");
- }
- }
-
- public static enum Type {
- BYTE,
- SHORT,
- INT,
- LONG,
- BIG_INTEGER
- }
-
- private Type tpe;
- private long longValue;
- private BigInteger biValue;
-
- public Type getType() {
- return tpe;
- }
-
- public void setByte(byte v){
- tpe = Type.BYTE;
- longValue = v;
- }
- public void setShort(short v) {
- tpe = Type.SHORT;
- longValue = v;
- }
- public void setInt(int v) {
- tpe = Type.INT;
- longValue = v;
- }
- public void setLong(long v) {
- tpe = Type.LONG;
- longValue = v;
- }
- public void setBigInteger(BigInteger v) {
- tpe = Type.BIG_INTEGER;
- biValue = v;
- }
-
- private RuntimeException failure() {
- return new IllegalStateException();
- }
-
- public boolean isBigInteger() {
- return tpe == Type.BIG_INTEGER;
- }
-
- @Override
- public boolean isValidByte() {
- return tpe == Type.BYTE;
- }
- @Override
- public boolean isValidShort() {
- return tpe.ordinal() <= Type.SHORT.ordinal();
- }
- @Override
- public boolean isValidInt() {
- return tpe.ordinal() <= Type.INT.ordinal();
- }
- @Override
- public boolean isValidLong() {
- return tpe.ordinal() <= Type.LONG.ordinal();
- }
-
- @Override
- public boolean isWhole() {
- return true;
- }
-
- public byte toByte() {
- return isBigInteger() ? biValue.byteValue() : (byte) longValue;
- }
-
- public short toShort() {
- return isBigInteger() ? biValue.shortValue() : (short) longValue;
- }
-
- public int toInt() {
- return isBigInteger() ? biValue.intValue() : (int) longValue;
- }
-
- public long toLong(){
- return isBigInteger() ? biValue.longValue() : longValue;
- }
-
- public BigInteger toBigInteger() {
- return isBigInteger() ? biValue : BigInteger.valueOf(longValue);
- }
- @Override
- public float toFloat() {
- return isBigInteger() ? biValue.floatValue() : (float) longValue;
- }
- @Override
- public double toDouble() {
- return isBigInteger() ? biValue.doubleValue() : (double) longValue;
- }
-
-
- @Override
- public byte asByte() throws MessageIntegerOverflowException {
- switch(tpe) {
- case BYTE:
- return (byte) longValue;
- case SHORT:
- case INT:
- case LONG:
- if(LongUtil.isValidByte(longValue)) {
- return (byte) longValue;
- }
- else {
- throw new MessageIntegerOverflowException(longValue);
- }
- case BIG_INTEGER:
- if(LongUtil.isValidByte(biValue)) {
- return biValue.byteValue();
- }
- else {
- throw new MessageIntegerOverflowException(biValue);
- }
- default:
- throw failure();
- }
- }
-
-
- @Override
- public short asShort() throws MessageIntegerOverflowException {
- switch(tpe) {
- case BYTE:
- case SHORT:
- return (short) longValue;
- case INT:
- case LONG:
- if(LongUtil.isValidShort(longValue)) {
- return (short) longValue;
- }
- else {
- throw new MessageIntegerOverflowException(longValue);
- }
- case BIG_INTEGER:
- if(LongUtil.isValidShort(biValue)) {
- return biValue.shortValue();
- }
- else {
- throw new MessageIntegerOverflowException(biValue);
- }
- default:
- throw failure();
- }
- }
-
-
- @Override
- public int asInt() throws MessageIntegerOverflowException {
- switch(tpe) {
- case BYTE:
- case SHORT:
- case INT:
- return (int) longValue;
- case LONG:
- if(LongUtil.isValidInt(longValue)) {
- return (int) longValue;
- }
- else {
- throw new MessageIntegerOverflowException(longValue);
- }
- case BIG_INTEGER:
- if(LongUtil.isValidInt(biValue)) {
- return biValue.intValue();
- }
- else {
- throw new MessageIntegerOverflowException(biValue);
- }
- default:
- throw failure();
- }
- }
-
- @Override
- public long asLong() throws MessageIntegerOverflowException {
- if(isBigInteger()){
- if(LongUtil.isValidLong(biValue)) {
- return biValue.longValue();
- } else {
- throw new MessageIntegerOverflowException(biValue);
- }
- }
- return longValue;
- }
-
- @Override
- public BigInteger asBigInteger() {
- return toBigInteger();
- }
-
-
- @Override
- public int hashCode() {
- return isBigInteger() ? biValue.hashCode() : (int) longValue;
- }
-
- @Override
- public String toString() {
- return isBigInteger() ? biValue.toString() : Long.toString(longValue);
- }
-
-
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/holder/RawHolder.java b/msgpack-core/src/main/java/org/msgpack/value/holder/RawHolder.java
deleted file mode 100644
index 371589957..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/holder/RawHolder.java
+++ /dev/null
@@ -1,270 +0,0 @@
-package org.msgpack.value.holder;
-
-import org.msgpack.core.MessagePack;
-import org.msgpack.core.MessagePacker;
-import org.msgpack.core.MessageStringCodingException;
-import org.msgpack.core.buffer.MessageBuffer;
-import org.msgpack.value.*;
-import org.msgpack.value.impl.AbstractValueRef;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import static org.msgpack.core.MessagePackException.UNREACHABLE;
-
-
-class RawHolderImpl extends AbstractValueRef implements RawValue {
-
- public static enum Type {
- STRING,
- BINARY
- }
-
- protected Type tpe;
- protected MessageBuffer buf;
-
- public void setString(MessageBuffer buf) {
- this.tpe = Type.STRING;
- this.buf = buf;
- }
-
- public void setBinary(MessageBuffer buf) {
- this.tpe = Type.BINARY;
- this.buf = buf;
- }
-
- public MessageBuffer getBuffer() { return buf; }
-
- @Override
- public byte[] toByteArray() {
- switch(tpe) {
- case STRING:
- case BINARY:
- return buf.toByteArray();
- default:
- throw UNREACHABLE;
- }
- }
-
- @Override
- public ByteBuffer toByteBuffer() {
- switch(tpe) {
- case STRING:
- return buf.toByteBuffer();
- default:
- throw UNREACHABLE;
- }
- }
-
- @Override
- public MessageBuffer toMessageBuffer() {
- return buf;
- }
-
- @Override
- public String toString() throws MessageStringCodingException {
- switch(tpe) {
- case STRING:
- return new String(buf.toByteArray(), MessagePack.UTF8);
- case BINARY:
- return buf.toHexString(0, buf.size());
- default:
- throw UNREACHABLE;
- }
- }
-
-
- @Override
- public ValueType getValueType() {
- switch(tpe) {
- case STRING:
- return ValueType.STRING;
- case BINARY:
- return ValueType.BINARY;
- default:
- throw UNREACHABLE;
- }
- }
-
- @Override
- public void writeTo(MessagePacker packer) throws IOException {
- switch(tpe) {
- case STRING:
- packer.packRawStringHeader(buf.size()).writePayload(buf.toByteBuffer());
- break;
- case BINARY:
- packer.packBinaryHeader(buf.size()).writePayload(buf.toByteBuffer());
- break;
- default:
- throw UNREACHABLE;
- }
- }
-
- @Override
- public void accept(ValueVisitor visitor) {
- switch(tpe) {
- case STRING:
- visitor.visitString(this.asString());
- break;
- case BINARY:
- visitor.visitBinary(this.asBinary());
- break;
- default:
- throw UNREACHABLE;
- }
- }
-
- @Override
- public RawValue toValue() {
- switch(tpe) {
- case STRING:
- return ValueFactory.newRawString(buf.toByteArray());
- case BINARY:
- return ValueFactory.newBinary(buf.toByteArray());
- default:
- throw UNREACHABLE;
- }
- }
-
-}
-
-
-/**
- * Holder of the raw values
- */
-public class RawHolder extends RawHolderImpl {
-
- private static class StringValueWrap extends RawHolderImpl implements StringValue {
- public StringValue toValue() {
- return ValueFactory.newRawString(buf.toByteArray());
- }
- }
-
- private static class BinaryValueWrap extends RawHolderImpl implements BinaryValue {
- public BinaryValue toValue() {
- return ValueFactory.newBinary(buf.toByteArray());
- }
- }
-
- private StringValueWrap stringWrap = new StringValueWrap();
- private BinaryValueWrap binaryWrap = new BinaryValueWrap();
-
- @Override
- public void setString(MessageBuffer buf) {
- this.tpe = Type.STRING;
- this.buf = buf;
- stringWrap.setString(buf);
- }
-
- @Override
- public void setBinary(MessageBuffer buf) {
- this.tpe = Type.BINARY;
- this.buf = buf;
- binaryWrap.setBinary(buf);
- }
-
- public MessageBuffer getBuffer() { return buf; }
-
- @Override
- public byte[] toByteArray() {
- switch(tpe) {
- case STRING:
- case BINARY:
- return buf.toByteArray();
- default:
- throw UNREACHABLE;
- }
- }
-
- @Override
- public ByteBuffer toByteBuffer() {
- switch(tpe) {
- case STRING:
- return buf.toByteBuffer();
- default:
- throw UNREACHABLE;
- }
- }
-
- @Override
- public MessageBuffer toMessageBuffer() {
- return buf;
- }
-
- @Override
- public String toString() throws MessageStringCodingException {
- switch(tpe) {
- case STRING:
- return new String(buf.toByteArray(), MessagePack.UTF8);
- case BINARY:
- return buf.toHexString(0, buf.size());
- default:
- throw UNREACHABLE;
- }
- }
-
-
- @Override
- public ValueType getValueType() {
- switch(tpe) {
- case STRING:
- return ValueType.STRING;
- case BINARY:
- return ValueType.BINARY;
- default:
- throw UNREACHABLE;
- }
- }
-
- @Override
- public void writeTo(MessagePacker packer) throws IOException {
- switch(tpe) {
- case STRING:
- packer.packRawStringHeader(buf.size()).writePayload(buf.toByteBuffer());
- break;
- case BINARY:
- packer.packBinaryHeader(buf.size()).writePayload(buf.toByteBuffer());
- break;
- default:
- throw UNREACHABLE;
- }
- }
-
- @Override
- public void accept(ValueVisitor visitor) {
- switch(tpe) {
- case STRING:
- visitor.visitString(this.asString());
- break;
- case BINARY:
- visitor.visitBinary(this.asBinary());
- break;
- default:
- throw UNREACHABLE;
- }
- }
-
- @Override
- public RawValue toValue() {
- switch(tpe) {
- case STRING:
- return ValueFactory.newRawString(buf.toByteArray());
- case BINARY:
- return ValueFactory.newBinary(buf.toByteArray());
- default:
- throw UNREACHABLE;
- }
- }
-
-
- @Override
- public StringValue asString() {
- return stringWrap;
- }
-
- @Override
- public BinaryValue asBinary() {
- return binaryWrap;
- }
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/holder/ValueHolder.java b/msgpack-core/src/main/java/org/msgpack/value/holder/ValueHolder.java
deleted file mode 100644
index 686bb710a..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/holder/ValueHolder.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package org.msgpack.value.holder;
-
-import org.msgpack.core.MessageUnpacker;
-import org.msgpack.core.buffer.MessageBuffer;
-import org.msgpack.value.ValueRef;
-import org.msgpack.value.impl.ArrayCursorImpl;
-import org.msgpack.value.Value;
-import org.msgpack.value.ValueFactory;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.impl.MapCursorImpl;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayDeque;
-
-import static org.msgpack.core.MessagePackException.UNREACHABLE;
-
-/**
- * This class can hold any message packed value.
- */
-public class ValueHolder {
-
- private ValueType vt;
- private IntegerHolder integerHolder = new IntegerHolder();
- private FloatHolder floatHolder = new FloatHolder();
- private RawHolder rawHolder = new RawHolder();
- private ExtHolder extHolder = new ExtHolder();
- private ArrayCursorImpl arrayCursor;
- private MapCursorImpl mapCursor;
- private ValueRef currentRef;
-
- public ValueRef getRef() {
- if(currentRef == null) {
- throw new IllegalStateException("no value is set to this holder");
- }
-
- return currentRef;
- }
-
- public Value get() {
- switch(vt) {
- case NIL:
- case BOOLEAN:
- case INTEGER:
- case FLOAT:
- case ARRAY:
- case MAP:
- case EXTENDED:
- return getRef().toValue();
- case STRING:
- return ValueFactory.newRawString(cloneBuffer(rawHolder.getBuffer()));
- case BINARY:
- return ValueFactory.newBinary(cloneBuffer(rawHolder.getBuffer()));
- default:
- throw UNREACHABLE;
- }
- }
-
-
- private static ByteBuffer cloneBuffer(MessageBuffer buffer) {
- return ByteBuffer.wrap(buffer.toByteArray());
- }
-
- public IntegerHolder getIntegerHolder() {
- return integerHolder;
- }
-
- public FloatHolder getFloatHolder() {
- return floatHolder;
- }
-
- public void setBoolean(boolean v) {
- vt = ValueType.BOOLEAN;
- currentRef = ValueFactory.newBoolean(v);
- }
-
- public void setNil() {
- vt = ValueType.NIL;
- currentRef = ValueFactory.nilValue();
- }
-
- public void setString(MessageBuffer rawString) {
- vt = ValueType.STRING;
- rawHolder.setString(rawString);
- currentRef = rawHolder.asString();
- }
-
- public void setBinary(MessageBuffer b) {
- vt = ValueType.BINARY;
- rawHolder.setBinary(b);
- currentRef = rawHolder.asBinary();
- }
-
- public void setToInteger() {
- vt = ValueType.INTEGER;
- currentRef = integerHolder;
- }
-
- public void setToFloat() {
- vt = ValueType.FLOAT;
- currentRef = floatHolder;
- }
-
- public void setExt(int extType, MessageBuffer b) {
- vt = ValueType.EXTENDED;
- extHolder.setExtType(extType, b);
- currentRef = extHolder;
- }
-
- public void prepareArrayCursor(MessageUnpacker unpacker) throws IOException {
- vt = ValueType.ARRAY;
-
- // TODO reusing cursor instances
- arrayCursor = new ArrayCursorImpl(new ValueHolder());
- arrayCursor.reset(unpacker);
- currentRef = arrayCursor;
- }
-
- public void prepareMapCursor(MessageUnpacker unpacker) throws IOException {
- vt = ValueType.MAP;
-
- // TODO reusing cursor instances
- mapCursor = new MapCursorImpl(new ValueHolder());
- mapCursor.reset(unpacker);
- currentRef = mapCursor;
- }
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractImmutableRawValue.java b/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractImmutableRawValue.java
new file mode 100644
index 000000000..31adb5b1a
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractImmutableRawValue.java
@@ -0,0 +1,185 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+
+import org.msgpack.core.MessagePack;
+import org.msgpack.core.MessageStringCodingException;
+import org.msgpack.value.ImmutableRawValue;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+import java.util.Arrays;
+
+public abstract class AbstractImmutableRawValue
+ extends AbstractImmutableValue
+ implements ImmutableRawValue
+{
+ protected final byte[] data;
+ private volatile String decodedStringCache;
+ private volatile CharacterCodingException codingException;
+
+ public AbstractImmutableRawValue(byte[] data)
+ {
+ this.data = data;
+ }
+
+ public AbstractImmutableRawValue(String string)
+ {
+ this.decodedStringCache = string;
+ this.data = string.getBytes(MessagePack.UTF8); // TODO
+ }
+
+ @Override
+ public ImmutableRawValue asRawValue()
+ {
+ return this;
+ }
+
+ @Override
+ public byte[] asByteArray()
+ {
+ return Arrays.copyOf(data, data.length);
+ }
+
+ @Override
+ public ByteBuffer asByteBuffer()
+ {
+ return ByteBuffer.wrap(data).asReadOnlyBuffer();
+ }
+
+ @Override
+ public String asString()
+ {
+ if (decodedStringCache == null) {
+ decodeString();
+ }
+ if (codingException != null) {
+ throw new MessageStringCodingException(codingException);
+ }
+ else {
+ return decodedStringCache;
+ }
+ }
+
+ @Override
+ public String toJson()
+ {
+ StringBuilder sb = new StringBuilder();
+ appendJsonString(sb, toString());
+ return sb.toString();
+ }
+
+ private void decodeString()
+ {
+ synchronized (data) {
+ if (decodedStringCache != null) {
+ return;
+ }
+ try {
+ CharsetDecoder reportDecoder = MessagePack.UTF8.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ this.decodedStringCache = reportDecoder.decode(asByteBuffer()).toString();
+ }
+ catch (CharacterCodingException ex) {
+ try {
+ CharsetDecoder replaceDecoder = MessagePack.UTF8.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE);
+ this.decodedStringCache = replaceDecoder.decode(asByteBuffer()).toString();
+ }
+ catch (CharacterCodingException neverThrown) {
+ throw new MessageStringCodingException(neverThrown);
+ }
+ this.codingException = ex;
+ }
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ if (decodedStringCache == null) {
+ decodeString();
+ }
+ return decodedStringCache;
+ }
+
+ static void appendJsonString(StringBuilder sb, String string)
+ {
+ sb.append("\"");
+ for (int i = 0; i < string.length(); i++) {
+ char ch = string.charAt(i);
+ if (ch < 0x20) {
+ switch (ch) {
+ case '\n':
+ sb.append("\\n");
+ break;
+ case '\r':
+ sb.append("\\r");
+ break;
+ case '\t':
+ sb.append("\\t");
+ break;
+ case '\f':
+ sb.append("\\f");
+ break;
+ case '\b':
+ sb.append("\\b");
+ break;
+ default:
+ // control chars
+ escapeChar(sb, ch);
+ break;
+ }
+ }
+ else if (ch <= 0x7f) {
+ switch (ch) {
+ case '\\':
+ sb.append("\\\\");
+ break;
+ case '"':
+ sb.append("\\\"");
+ break;
+ default:
+ sb.append(ch);
+ break;
+ }
+ }
+ else if (ch >= 0xd800 && ch <= 0xdfff) {
+ // surrogates
+ escapeChar(sb, ch);
+ }
+ else {
+ sb.append(ch);
+ }
+ }
+ sb.append("\"");
+ }
+
+ private static final char[] HEX_TABLE = "0123456789ABCDEF".toCharArray();
+
+ private static void escapeChar(StringBuilder sb, int ch)
+ {
+ sb.append("\\u");
+ sb.append(HEX_TABLE[(ch >> 12) & 0x0f]);
+ sb.append(HEX_TABLE[(ch >> 8) & 0x0f]);
+ sb.append(HEX_TABLE[(ch >> 4) & 0x0f]);
+ sb.append(HEX_TABLE[ch & 0x0f]);
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractImmutableValue.java b/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractImmutableValue.java
new file mode 100644
index 000000000..1dae99cf2
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractImmutableValue.java
@@ -0,0 +1,166 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+
+import org.msgpack.core.MessageTypeCastException;
+import org.msgpack.value.ImmutableArrayValue;
+import org.msgpack.value.ImmutableBinaryValue;
+import org.msgpack.value.ImmutableBooleanValue;
+import org.msgpack.value.ImmutableExtensionValue;
+import org.msgpack.value.ImmutableFloatValue;
+import org.msgpack.value.ImmutableIntegerValue;
+import org.msgpack.value.ImmutableMapValue;
+import org.msgpack.value.ImmutableNilValue;
+import org.msgpack.value.ImmutableNumberValue;
+import org.msgpack.value.ImmutableRawValue;
+import org.msgpack.value.ImmutableStringValue;
+import org.msgpack.value.ImmutableValue;
+
+abstract class AbstractImmutableValue
+ implements ImmutableValue
+{
+ @Override
+ public boolean isNilValue()
+ {
+ return getValueType().isNilType();
+ }
+
+ @Override
+ public boolean isBooleanValue()
+ {
+ return getValueType().isBooleanType();
+ }
+
+ @Override
+ public boolean isNumberValue()
+ {
+ return getValueType().isNumberType();
+ }
+
+ @Override
+ public boolean isIntegerValue()
+ {
+ return getValueType().isIntegerType();
+ }
+
+ @Override
+ public boolean isFloatValue()
+ {
+ return getValueType().isFloatType();
+ }
+
+ @Override
+ public boolean isRawValue()
+ {
+ return getValueType().isRawType();
+ }
+
+ @Override
+ public boolean isBinaryValue()
+ {
+ return getValueType().isBinaryType();
+ }
+
+ @Override
+ public boolean isStringValue()
+ {
+ return getValueType().isStringType();
+ }
+
+ @Override
+ public boolean isArrayValue()
+ {
+ return getValueType().isArrayType();
+ }
+
+ @Override
+ public boolean isMapValue()
+ {
+ return getValueType().isMapType();
+ }
+
+ @Override
+ public boolean isExtensionValue()
+ {
+ return getValueType().isExtensionType();
+ }
+
+ @Override
+ public ImmutableNilValue asNilValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ImmutableBooleanValue asBooleanValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ImmutableNumberValue asNumberValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ImmutableIntegerValue asIntegerValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ImmutableFloatValue asFloatValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ImmutableRawValue asRawValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ImmutableBinaryValue asBinaryValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ImmutableStringValue asStringValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ImmutableArrayValue asArrayValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ImmutableMapValue asMapValue()
+ {
+ throw new MessageTypeCastException();
+ }
+
+ @Override
+ public ImmutableExtensionValue asExtensionValue()
+ {
+ throw new MessageTypeCastException();
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractValue.java b/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractValue.java
deleted file mode 100644
index c74dc38a7..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractValue.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessageTypeException;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.*;
-
-/**
-* Base implementation of MessagePackValue
-*/
-public abstract class AbstractValue extends AbstractValueRef implements Value {
-
- @Override
- public boolean isRef() { return false; }
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractValueRef.java b/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractValueRef.java
deleted file mode 100644
index 1e67a6b3c..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/AbstractValueRef.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessageTypeException;
-import org.msgpack.value.*;
-
-/**
- * Base implementation of message pack values
- */
-public abstract class AbstractValueRef implements ValueRef {
-
- public boolean isRef() { return true; }
-
- protected static int NUMBER_TYPE_MASK = (1 << ValueType.INTEGER.ordinal())
- | (1 << ValueType.FLOAT.ordinal());
- protected static int RAW_TYPE_MASK = (1 << ValueType.STRING.ordinal())
- | (1 << ValueType.BINARY.ordinal());
-
- protected E as(Class valueClass, ValueType vt) {
- return as(valueClass, 1 << vt.ordinal());
- }
- protected E as(Class valueClass, int bitMask) {
- if(this.getValueType() == null)
- throw new MessageTypeException("This value points to nothing");
- if(!this.getValueType().isTypeOf(bitMask))
- throw new MessageTypeException(String.format("Expected %s, but %s", valueClass.getSimpleName(), this.getValueType()));
- return valueClass.cast(this);
- }
-
- public NilValue asNil() throws MessageTypeException { return as(NilValue.class, ValueType.NIL); }
- public BooleanValue asBoolean() throws MessageTypeException{ return as(BooleanValue.class, ValueType.BOOLEAN); }
- public NumberValue asNumber() throws MessageTypeException { return as(NumberValue.class, NUMBER_TYPE_MASK); }
- public IntegerValue asInteger() throws MessageTypeException { return as(IntegerValue.class, ValueType.INTEGER); }
- public FloatValue asFloat() throws MessageTypeException { return as(FloatValue.class, ValueType.FLOAT); }
- public BinaryValue asBinary() throws MessageTypeException { return as(BinaryValue.class, ValueType.BINARY); }
- public StringValue asString() throws MessageTypeException { return as(StringValue.class, ValueType.STRING); }
- public RawValue asRaw() throws MessageTypeException { return as(RawValue.class, RAW_TYPE_MASK); }
- public ArrayValue asArrayValue() throws MessageTypeException { return as(ArrayValue.class, ValueType.ARRAY); }
- public MapValue asMapValue() throws MessageTypeException { return as(MapValue.class, ValueType.MAP); }
- public ExtendedValue asExtended() throws MessageTypeException { return as(ExtendedValue.class, ValueType.EXTENDED); }
-
- @Override
- public ArrayCursor getArrayCursor() throws MessageTypeException {
- throw new MessageTypeException("This value is not an array type");
- }
- @Override
- public MapCursor getMapCursor() throws MessageTypeException {
- throw new MessageTypeException("This value is not a map type");
- }
-
- public boolean isNil() { return getValueType().isNilType(); }
- public boolean isBoolean() { return getValueType().isBooleanType(); }
- public boolean isNumber() { return getValueType().isNumberType(); }
- public boolean isInteger() { return getValueType().isIntegerType(); }
- public boolean isFloat() { return getValueType().isFloatType(); }
- public boolean isBinary() { return getValueType().isBinaryType(); }
- public boolean isString() { return getValueType().isStringType(); }
- public boolean isRaw() { return getValueType().isRawType(); }
- public boolean isArray() { return getValueType().isArrayType(); }
- public boolean isMap() { return getValueType().isMapType(); }
- public boolean isExtended() { return getValueType().isExtendedType(); }
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ArrayCursorImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ArrayCursorImpl.java
deleted file mode 100644
index 5a5a1676e..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/ArrayCursorImpl.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.*;
-import org.msgpack.value.*;
-import org.msgpack.value.holder.ValueHolder;
-import static org.msgpack.core.MessagePackException.UNSUPPORTED;
-
-import java.io.IOException;
-import java.util.Iterator;
-
-/**
- * Created on 6/16/14.
- */
-public class ArrayCursorImpl extends AbstractValueRef implements ArrayCursor {
-
- private final ValueHolder valueHolder;
- private MessageUnpacker unpacker;
- private int cursor = 0;
- private int arraySize;
-
- public ArrayCursorImpl(ValueHolder valueHolder) {
- this.valueHolder = valueHolder;
- }
-
- public void reset(MessageUnpacker unpacker) throws IOException {
- this.unpacker = unpacker;
- this.arraySize = unpacker.unpackArrayHeader();
- this.cursor = 0;
- }
-
- @Override
- public int size() {
- return arraySize;
- }
-
- @Override
- public Iterator iterator() {
- return new Iterator() {
- @Override
- public boolean hasNext() {
- return ArrayCursorImpl.this.hasNext();
- }
- @Override
- public ValueRef next() {
- return ArrayCursorImpl.this.next();
- }
- @Override
- public void remove() {
- throw UNSUPPORTED("remove");
- }
- };
- }
-
- public boolean hasNext() {
- return cursor < arraySize;
- }
-
- public ValueRef next() {
- try {
- unpacker.unpackValue(valueHolder);
- cursor++;
- return valueHolder.getRef();
- }
- catch(IOException e) {
- throw new MessageFormatException(e);
- }
- }
-
- @Override
- public void skip() {
- try {
- unpacker.skipValue();
- cursor++;
- }
- catch(IOException e) {
- throw new MessageFormatException(e);
- }
- }
-
- @Override
- public void skipAll() {
- while(hasNext()) {
- skip();
- }
- }
-
-
- private void ensureNotTraversed() {
- if(cursor != 0)
- throw UNSUPPORTED("ArrayCursor is already traversed");
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.ARRAY;
- }
-
- @Override
- public ArrayCursor getArrayCursor() throws MessageTypeException {
- return this;
- }
-
- @Override
- public void writeTo(MessagePacker packer) throws IOException {
- ensureNotTraversed();
- packer.packArrayHeader(arraySize);
- for(ValueRef v : this) {
- packer.packValue(v.toValue());
- }
- }
-
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitArray(toValue());
- }
-
- @Override
- public ArrayValue toValue() {
- Value[] arr = new Value[arraySize];
- int i = 0;
- for(ValueRef v : this) {
- arr[i++] = v.toValue();
- }
- return ValueFactory.newArray(arr);
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ArrayValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ArrayValueImpl.java
deleted file mode 100644
index eda927bed..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/ArrayValueImpl.java
+++ /dev/null
@@ -1,163 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessagePacker;
-import org.msgpack.core.MessageTypeException;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.*;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Iterator;
-
-/**
-* Created on 5/30/14.
-*/
-public class ArrayValueImpl extends AbstractValue implements ArrayValue {
- private static ArrayValueImpl EMPTY = new ArrayValueImpl(new Value[0]);
-
- public static ArrayValue empty() {
- return EMPTY;
- }
-
- private int cursor = 0;
- private final Value[] array;
-
- public ArrayValueImpl(Value[] array) {
- this.array = array;
- }
-
- public Value get(int index) {
- return array[index];
- }
-
- public Value apply(int index) {
- return array[index];
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.ARRAY;
- }
-
-
- @Override
- public ArrayCursor getArrayCursor() throws MessageTypeException {
- return this;
- }
-
- @Override
- public void writeTo(MessagePacker pk) throws IOException {
- pk.packArrayHeader(array.length);
- for(int i = 0; i < array.length; i++) {
- array[i].writeTo(pk);
- }
- }
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitArray(this);
- }
- @Override
- public ArrayValue toValue() {
- return this;
- }
-
- @Override
- public boolean equals(Object o) {
- if(o == this) {
- return true;
- }
- if(!(o instanceof Value)) {
- return false;
- }
- Value v = (Value) o;
- if(!v.isArray()) {
- return false;
- }
- Value[] other = v.asArrayValue().toValueArray();
- if(array.length != other.length)
- return false;
-
- for(int i = 0; i < array.length; i++) {
- if(!array[i].equals(other[i])) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public int hashCode() {
- int h = 1;
- for(int i = 0; i < array.length; i++) {
- Value obj = array[i];
- h = 31 * h + obj.hashCode();
- }
- return h;
- }
-
- @Override
- public String toString() {
- return toString(new StringBuilder()).toString();
- }
-
- private StringBuilder toString(StringBuilder sb) {
- if(array.length == 0) {
- return sb.append("[]");
- }
- sb.append("[");
- sb.append(array[0]);
- for(int i = 1; i < array.length; i++) {
- sb.append(",");
- sb.append(array[i].toString());
- }
- sb.append("]");
- return sb;
- }
-
- @Override
- public int size() {
- return array.length;
- }
-
- @Override
- public boolean hasNext() {
- return cursor < array.length;
- }
- @Override
- public ValueRef next() {
- return array[cursor++];
- }
- @Override
- public void skip() {
- cursor++;
- }
- @Override
- public void skipAll() {
- while(hasNext()) {
- skip();
- }
- }
-
- public Value[] toValueArray() {
- return Arrays.copyOf(array, array.length);
- }
-
- @Override
- public Iterator iterator() {
- return new Iterator() {
- int cursor = 0;
- @Override
- public boolean hasNext() {
- return cursor < array.length;
- }
- @Override
- public ValueRef next() {
- return array[cursor++];
- }
- @Override
- public void remove() {
- throw new UnsupportedOperationException("remove");
- }
- };
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/BinaryValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/BinaryValueImpl.java
deleted file mode 100644
index eae7f33eb..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/BinaryValueImpl.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.value.Value;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.BinaryValue;
-import org.msgpack.value.ValueVisitor;
-
-import java.nio.ByteBuffer;
-
-/**
-* Created on 5/30/14.
-*/
-public class BinaryValueImpl extends RawValueImpl implements BinaryValue {
- public BinaryValueImpl(ByteBuffer byteBuffer) {
- super(byteBuffer);
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.BINARY;
- }
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitBinary(this);
- }
-
- @Override
- public BinaryValue toValue() {
- return this;
- }
-
-
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/BooleanValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/BooleanValueImpl.java
deleted file mode 100644
index 0421ef8e0..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/BooleanValueImpl.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessagePacker;
-import org.msgpack.value.Value;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.BooleanValue;
-import org.msgpack.value.ValueVisitor;
-
-import java.io.IOException;
-
-/**
-* Created on 5/30/14.
-*/
-public class BooleanValueImpl extends AbstractValue implements BooleanValue {
-
- public static BooleanValue TRUE = new BooleanValueImpl(true);
- public static BooleanValue FALSE = new BooleanValueImpl(false);
-
- private final boolean value;
-
- public BooleanValueImpl(boolean value) {
- this.value = value;
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.BOOLEAN;
- }
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof BooleanValue))
- return false;
- return value == ((BooleanValue) o).toBoolean();
- }
-
- @Override
- public int hashCode() {
- return 0;
- }
-
- public String toString() {
- return Boolean.toString(value);
- }
-
- @Override
- public boolean toBoolean() {
- return value;
- }
-
- @Override
- public void writeTo(MessagePacker packer) throws IOException {
- packer.packBoolean(value);
- }
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitBoolean(value);
- }
- @Override
- public BooleanValue toValue() {
- return this;
- }
-
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/CursorImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/CursorImpl.java
deleted file mode 100644
index 754ef4700..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/CursorImpl.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.*;
-import org.msgpack.value.*;
-import org.msgpack.value.holder.ValueHolder;
-import java.io.IOException;
-import java.util.NoSuchElementException;
-
-/**
- * Cursor implementation
- */
-public class CursorImpl implements Cursor {
-
- private final MessageUnpacker unpacker;
- private MessageFormat currentFormat;
- private ValueHolder valueHolder;
-
- public CursorImpl(MessageUnpacker unpacker) {
- this.unpacker = unpacker;
- this.currentFormat = MessageFormat.NIL;
- this.valueHolder = new ValueHolder();
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException("remove");
- }
-
-
- @Override
- public boolean hasNext() {
- try {
- return unpacker.hasNext();
- }
- catch(IOException e) {
- throw new MessageFormatException(e);
- }
- }
-
- @Override
- public void skip() {
- try {
- unpacker.skipValue();
- }
- catch(IOException e){
- throw new MessageFormatException(e);
- }
- }
- @Override
- public long getReadBytes() {
- return unpacker.getTotalReadBytes();
- }
-
-
- private final void readNext() {
- try {
- currentFormat = unpacker.unpackValue(valueHolder);
- }
- catch(IOException e) {
- throw new MessageFormatException(e);
- }
- }
-
- @Override
- public Value next() {
- readNext();
- return valueHolder.get();
- }
-
- @Override
- public ValueRef nextRef() {
- readNext();
- return valueHolder.getRef();
- }
-
- private ValueType getValueType() {
- return currentFormat.getValueType();
- }
-
-
- @Override
- public Out apply(Function f) {
- return null;
- }
-
- @Override
- public boolean isNilValue() {
- return getValueType().isNilType();
- }
- @Override
- public boolean isBooleanValue() {
- return getValueType().isBooleanType();
- }
- @Override
- public boolean isNumberValue() {
- return getValueType().isNumberType();
- }
- @Override
- public boolean isIntegerValue() {
- return getValueType().isIntegerType();
- }
- @Override
- public boolean isFloatValue() {
- return getValueType().isFloatType();
- }
- @Override
- public boolean isBinaryValue() {
- return getValueType().isBinaryType();
- }
- @Override
- public boolean isStringValue() {
- return getValueType().isStringType();
- }
- @Override
- public boolean isRawValue() {
- return getValueType().isRawType();
- }
- @Override
- public boolean isArrayValue() {
- return getValueType().isArrayType();
- }
- @Override
- public boolean isMapValue() {
- return getValueType().isMapType();
- }
- @Override
- public boolean isExtendedValue() {
- return getValueType().isExtendedType();
- }
-
- @Override
- public void close() throws IOException {
- unpacker.close();
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/DoubleValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/DoubleValueImpl.java
deleted file mode 100644
index e8382a344..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/DoubleValueImpl.java
+++ /dev/null
@@ -1,152 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessageFloatOverflowException;
-import org.msgpack.core.MessageOverflowException;
-import org.msgpack.core.MessagePacker;
-import org.msgpack.value.*;
-
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-
-/**
-* Created on 5/30/14.
-*/
-public class DoubleValueImpl extends AbstractValue implements FloatValue {
- private final double value;
-
- public DoubleValueImpl(double value) {
- this.value = value;
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.FLOAT;
- }
-
- @Override
- public boolean isValidByte() {
- return ((double) ((byte) value)) == value;
- }
- @Override
- public boolean isValidShort() {
- return ((double) ((short) value)) == value;
- }
- @Override
- public boolean isValidInt() {
- return ((double) ((int) value)) == value;
- }
- @Override
- public boolean isValidLong() {
- long l = (long) value;
- return ((double) l) == value && l != Long.MAX_VALUE;
- }
- @Override
- public boolean isWhole() {
- long l = (long) value;
- return ((double) l == value) || l == Long.MAX_VALUE && value < Double.POSITIVE_INFINITY || l == Long.MIN_VALUE && value > Double.NEGATIVE_INFINITY;
- }
- @Override
- public byte toByte() {
- return (byte) value;
- }
-
- @Override
- public short toShort() {
- return (short) value;
- }
-
- @Override
- public int toInt() {
- return (int) value;
- }
-
- @Override
- public long toLong() {
- return (long) value;
- }
-
- @Override
- public BigInteger toBigInteger() {
- return new BigDecimal(value).toBigInteger();
- }
-
- @Override
- public float toFloat() {
- return (float) value;
- }
-
- @Override
- public double toDouble() {
- return value;
- }
- @Override
- public byte asByte() throws MessageOverflowException {
- if(!isValidByte())
- throw new MessageFloatOverflowException(value);
- return (byte) value;
- }
- @Override
- public short asShort() throws MessageOverflowException {
- if(!isValidShort())
- throw new MessageFloatOverflowException(value);
- return (short) value;
- }
- @Override
- public int asInt() throws MessageOverflowException {
- if(!isValidInt())
- throw new MessageFloatOverflowException(value);
- return (int) value;
- }
- @Override
- public long asLong() throws MessageOverflowException {
- if(!isValidLong())
- throw new MessageFloatOverflowException(value);
- return (long) value;
- }
- @Override
- public BigInteger asBigInteger() throws MessageOverflowException {
- if(!isWhole())
- throw new MessageFloatOverflowException(value);
- return new BigDecimal(value).toBigInteger();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof Value)) {
- return false;
- }
- Value v = (Value) o;
- if (!v.isFloat()) {
- return false;
- }
- return value == v.asFloat().toDouble();
- }
-
- @Override
- public FloatValue toValue() {
- return ValueFactory.newDouble(value);
- }
- @Override
- public void writeTo(MessagePacker pk) throws IOException {
- pk.packDouble(value);
- }
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitFloat(this);
- }
-
- @Override
- public int hashCode() {
- long v = Double.doubleToLongBits(value);
- return (int) (v ^ (v >>> 32));
- }
-
- @Override
- public String toString() {
- return Double.toString(value);
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ExtendedValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ExtendedValueImpl.java
deleted file mode 100644
index 80a28fd78..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/ExtendedValueImpl.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessagePacker;
-import org.msgpack.value.Value;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.ExtendedValue;
-import org.msgpack.value.ValueVisitor;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Extended value implementation
- */
-public class ExtendedValueImpl extends RawValueImpl implements ExtendedValue {
-
- private final int type;
-
-
- public ExtendedValueImpl(int type, ByteBuffer data) {
- super(data);
- this.type = type;
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.EXTENDED;
- }
- @Override
- public void writeTo(MessagePacker packer) throws IOException {
- packer.packExtendedTypeHeader(type, byteBuffer.remaining());
- packer.writePayload(byteBuffer);
- }
-
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitExtended(this);
- }
-
- @Override
- public ExtendedValue toValue() {
- return this;
- }
-
- @Override
- public int getExtType() {
- return type;
- }
-
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/FloatValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/FloatValueImpl.java
deleted file mode 100644
index 644b81aaa..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/FloatValueImpl.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessageFloatOverflowException;
-import org.msgpack.core.MessageOverflowException;
-import org.msgpack.core.MessagePacker;
-import org.msgpack.value.*;
-
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-
-/**
-* Created on 5/30/14.
-*/
-public class FloatValueImpl extends AbstractValue implements FloatValue {
- private final float value;
-
- public FloatValueImpl(float value) {
- this.value = value;
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.FLOAT;
- }
-
- @Override
- public boolean isValidByte() {
- return (float) ((byte) value) == value;
- }
- @Override
- public boolean isValidShort() {
- return (float) ((short) value) == value;
- }
- @Override
- public boolean isValidInt() {
- int i = (int) value;
- return ((float) i) == value && i != Integer.MAX_VALUE;
- }
- @Override
- public boolean isValidLong() {
- long l = (long) value;
- return ((float) l) == value && l != Long.MAX_VALUE;
- }
-
- @Override
- public boolean isWhole() {
- long l = (long) value;
- return ((float) l) == value || l == Long.MAX_VALUE || value < Float.POSITIVE_INFINITY || l == Long.MIN_VALUE && value > Float.NEGATIVE_INFINITY;
- }
-
- @Override
- public byte toByte() {
- return (byte) value;
- }
-
- @Override
- public short toShort() {
- return (short) value;
- }
-
- @Override
- public int toInt() {
- return (int) value;
- }
-
- @Override
- public long toLong() {
- return (long) value;
- }
-
- @Override
- public BigInteger toBigInteger() {
- return new BigDecimal((double) value).toBigInteger();
- }
-
- @Override
- public float toFloat() {
- return value;
- }
-
- @Override
- public double toDouble() {
- return (double) value;
- }
- @Override
- public byte asByte() throws MessageOverflowException {
- if (!isValidByte()) {
- throw new MessageFloatOverflowException(value);
- }
- return (byte) value;
- }
- @Override
- public short asShort() throws MessageOverflowException {
- if(!isValidShort())
- throw new MessageFloatOverflowException(value);
- return (short) value;
- }
- @Override
- public int asInt() throws MessageOverflowException {
- if(!isValidInt())
- throw new MessageFloatOverflowException(value);
- return (int) value;
- }
-
- @Override
- public long asLong() throws MessageOverflowException {
- if(!isValidLong())
- throw new MessageFloatOverflowException(value);
- return (long) value;
- }
-
- @Override
- public BigInteger asBigInteger() throws MessageOverflowException {
- if(!isWhole())
- throw new MessageFloatOverflowException(value);
-
- return BigDecimal.valueOf(value).toBigInteger();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof Value)) {
- return false;
- }
- Value v = (Value) o;
- if (!v.isFloat()) {
- return false;
- }
- return (double) value == v.asFloat().toDouble();
- }
-
- @Override
- public void writeTo(MessagePacker pk) throws IOException {
- pk.packFloat(value);
- }
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitFloat(this);
- }
- @Override
- public FloatValue toValue() {
- return this;
- }
-
- @Override
- public int hashCode() {
- long v = Double.doubleToLongBits((double) value);
- return (int) (v ^ (v >>> 32));
- }
-
- @Override
- public String toString() {
- return Float.toString(value);
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableArrayValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableArrayValueImpl.java
new file mode 100644
index 000000000..3e1b732c2
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableArrayValueImpl.java
@@ -0,0 +1,263 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+
+import org.msgpack.core.MessagePacker;
+import org.msgpack.value.ArrayValue;
+import org.msgpack.value.ImmutableArrayValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueType;
+
+import java.io.IOException;
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * {@code ImmutableArrayValueImpl} Implements {@code ImmutableArrayValue} using a {@code Value[]} field.
+ *
+ * @see org.msgpack.value.IntegerValue
+ */
+public class ImmutableArrayValueImpl
+ extends AbstractImmutableValue
+ implements ImmutableArrayValue
+{
+ private static final ImmutableArrayValueImpl EMPTY = new ImmutableArrayValueImpl(new Value[0]);
+
+ public static ImmutableArrayValue empty()
+ {
+ return EMPTY;
+ }
+
+ private final Value[] array;
+
+ public ImmutableArrayValueImpl(Value[] array)
+ {
+ this.array = array;
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.ARRAY;
+ }
+
+ @Override
+ public ImmutableArrayValue immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableArrayValue asArrayValue()
+ {
+ return this;
+ }
+
+ @Override
+ public int size()
+ {
+ return array.length;
+ }
+
+ @Override
+ public Value get(int index)
+ {
+ return array[index];
+ }
+
+ @Override
+ public Value getOrNilValue(int index)
+ {
+ if (index < array.length && index >= 0) {
+ return array[index];
+ }
+ return ImmutableNilValueImpl.get();
+ }
+
+ @Override
+ public Iterator iterator()
+ {
+ return new Ite(array);
+ }
+
+ @Override
+ public List list()
+ {
+ return new ImmutableArrayValueList(array);
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ pk.packArrayHeader(array.length);
+ for (int i = 0; i < array.length; i++) {
+ array[i].writeTo(pk);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Value)) {
+ return false;
+ }
+ Value v = (Value) o;
+
+ if (v instanceof ImmutableArrayValueImpl) {
+ ImmutableArrayValueImpl oa = (ImmutableArrayValueImpl) v;
+ return Arrays.equals(array, oa.array);
+ }
+ else {
+ if (!v.isArrayValue()) {
+ return false;
+ }
+ ArrayValue av = v.asArrayValue();
+ if (size() != av.size()) {
+ return false;
+ }
+ Iterator oi = av.iterator();
+ int i = 0;
+ while (i < array.length) {
+ if (!oi.hasNext() || !array[i].equals(oi.next())) {
+ return false;
+ }
+ i++;
+ }
+ return true;
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int h = 1;
+ for (int i = 0; i < array.length; i++) {
+ Value obj = array[i];
+ h = 31 * h + obj.hashCode();
+ }
+ return h;
+ }
+
+ @Override
+ public String toJson()
+ {
+ if (array.length == 0) {
+ return "[]";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ sb.append(array[0].toJson());
+ for (int i = 1; i < array.length; i++) {
+ sb.append(",");
+ sb.append(array[i].toJson());
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public String toString()
+ {
+ if (array.length == 0) {
+ return "[]";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ appendString(sb, array[0]);
+ for (int i = 1; i < array.length; i++) {
+ sb.append(",");
+ appendString(sb, array[i]);
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ private static void appendString(StringBuilder sb, Value value)
+ {
+ if (value.isRawValue()) {
+ sb.append(value.toJson());
+ }
+ else {
+ sb.append(value.toString());
+ }
+ }
+
+ private static class ImmutableArrayValueList
+ extends AbstractList
+ {
+ private final Value[] array;
+
+ public ImmutableArrayValueList(Value[] array)
+ {
+ this.array = array;
+ }
+
+ @Override
+ public Value get(int index)
+ {
+ return array[index];
+ }
+
+ @Override
+ public int size()
+ {
+ return array.length;
+ }
+ }
+
+ private static class Ite
+ implements Iterator
+ {
+ private final Value[] array;
+ private int index;
+
+ public Ite(Value[] array)
+ {
+ this.array = array;
+ this.index = 0;
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return index != array.length;
+ }
+
+ @Override
+ public Value next()
+ {
+ int i = index;
+ if (i >= array.length) {
+ throw new NoSuchElementException();
+ }
+ index = i + 1;
+ return array[i];
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/BigIntegerValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableBigIntegerValueImpl.java
similarity index 51%
rename from msgpack-core/src/main/java/org/msgpack/value/impl/BigIntegerValueImpl.java
rename to msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableBigIntegerValueImpl.java
index 85af4a176..c6fe39386 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/BigIntegerValueImpl.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableBigIntegerValueImpl.java
@@ -1,26 +1,65 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+import org.msgpack.core.MessageFormat;
import org.msgpack.core.MessageIntegerOverflowException;
import org.msgpack.core.MessagePacker;
-import org.msgpack.value.ValueType;
+import org.msgpack.value.ImmutableIntegerValue;
+import org.msgpack.value.ImmutableNumberValue;
import org.msgpack.value.IntegerValue;
import org.msgpack.value.Value;
-import org.msgpack.value.ValueVisitor;
+import org.msgpack.value.ValueType;
import java.io.IOException;
import java.math.BigInteger;
-import static org.msgpack.core.Preconditions.checkNotNull;
-
/**
-* Created on 5/30/14.
-*/
-public class BigIntegerValueImpl extends AbstractValue implements IntegerValue {
+ * {@code ImmutableBigIntegerValueImpl} Implements {@code ImmutableBigIntegerValue} using a {@code BigInteger} field.
+ *
+ * @see org.msgpack.value.IntegerValue
+ */
+public class ImmutableBigIntegerValueImpl
+ extends AbstractImmutableValue
+ implements ImmutableIntegerValue
+{
+ public static MessageFormat mostSuccinctMessageFormat(IntegerValue v)
+ {
+ if (v.isInByteRange()) {
+ return MessageFormat.INT8;
+ }
+ else if (v.isInShortRange()) {
+ return MessageFormat.INT16;
+ }
+ else if (v.isInIntRange()) {
+ return MessageFormat.INT32;
+ }
+ else if (v.isInLongRange()) {
+ return MessageFormat.INT64;
+ }
+ else {
+ return MessageFormat.UINT64;
+ }
+ }
private final BigInteger value;
- public BigIntegerValueImpl(BigInteger value) {
- this.value = checkNotNull(value, "BigInteger value is null");
+ public ImmutableBigIntegerValueImpl(BigInteger value)
+ {
+ this.value = value;
}
private static final BigInteger BYTE_MIN = BigInteger.valueOf((long) Byte.MIN_VALUE);
@@ -33,121 +72,153 @@ public BigIntegerValueImpl(BigInteger value) {
private static final BigInteger LONG_MAX = BigInteger.valueOf((long) Long.MAX_VALUE);
@Override
- public ValueType getValueType() {
+ public ValueType getValueType()
+ {
return ValueType.INTEGER;
}
@Override
- public byte toByte() {
+ public ImmutableIntegerValue immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableNumberValue asNumberValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableIntegerValue asIntegerValue()
+ {
+ return this;
+ }
+
+ @Override
+ public byte toByte()
+ {
return value.byteValue();
}
@Override
- public short toShort() {
+ public short toShort()
+ {
return value.shortValue();
}
@Override
- public int toInt() {
+ public int toInt()
+ {
return value.intValue();
}
@Override
- public long toLong() {
+ public long toLong()
+ {
return value.longValue();
}
@Override
- public BigInteger toBigInteger() {
+ public BigInteger toBigInteger()
+ {
return value;
}
@Override
- public float toFloat() {
+ public float toFloat()
+ {
return value.floatValue();
}
@Override
- public double toDouble() {
+ public double toDouble()
+ {
return value.doubleValue();
}
@Override
- public byte asByte() throws MessageIntegerOverflowException {
- if (!isValidByte()) {
- throw new MessageIntegerOverflowException(value);
- }
- return value.byteValue();
+ public boolean isInByteRange()
+ {
+ return 0 <= value.compareTo(BYTE_MIN) && value.compareTo(BYTE_MAX) <= 0;
}
@Override
- public short asShort() throws MessageIntegerOverflowException {
- if (!isValidShort()) {
- throw new MessageIntegerOverflowException(value);
- }
- return value.shortValue();
+ public boolean isInShortRange()
+ {
+ return 0 <= value.compareTo(SHORT_MIN) && value.compareTo(SHORT_MAX) <= 0;
}
@Override
- public int asInt() throws MessageIntegerOverflowException {
- if (!isValidInt()) {
- throw new MessageIntegerOverflowException(value);
- }
- return value.intValue();
+ public boolean isInIntRange()
+ {
+ return 0 <= value.compareTo(INT_MIN) && value.compareTo(INT_MAX) <= 0;
}
@Override
- public long asLong() throws MessageIntegerOverflowException {
- if (!isValidLong()) {
- throw new MessageIntegerOverflowException(value);
- }
- return value.longValue();
+ public boolean isInLongRange()
+ {
+ return 0 <= value.compareTo(LONG_MIN) && value.compareTo(LONG_MAX) <= 0;
}
@Override
- public BigInteger asBigInteger() throws MessageIntegerOverflowException {
- return value;
+ public MessageFormat mostSuccinctMessageFormat()
+ {
+ return mostSuccinctMessageFormat(this);
}
@Override
- public boolean isValidByte() {
- return 0 <= value.compareTo(BYTE_MIN) && value.compareTo(BYTE_MAX) <= 0;
+ public byte asByte()
+ {
+ if (!isInByteRange()) {
+ throw new MessageIntegerOverflowException(value);
+ }
+ return value.byteValue();
}
@Override
- public boolean isValidShort() {
- return 0 <= value.compareTo(SHORT_MIN) && value.compareTo(SHORT_MAX) <= 0;
+ public short asShort()
+ {
+ if (!isInShortRange()) {
+ throw new MessageIntegerOverflowException(value);
+ }
+ return value.shortValue();
}
@Override
- public boolean isValidInt() {
- return 0 <= value.compareTo(INT_MIN) && value.compareTo(INT_MAX) <= 0;
+ public int asInt()
+ {
+ if (!isInIntRange()) {
+ throw new MessageIntegerOverflowException(value);
+ }
+ return value.intValue();
}
@Override
- public boolean isValidLong() {
- return 0 <= value.compareTo(LONG_MIN) && value.compareTo(LONG_MAX) <= 0;
+ public long asLong()
+ {
+ if (!isInLongRange()) {
+ throw new MessageIntegerOverflowException(value);
+ }
+ return value.longValue();
}
+
@Override
- public boolean isWhole() {
- return true;
+ public BigInteger asBigInteger()
+ {
+ return value;
}
@Override
- public void writeTo(MessagePacker pk) throws IOException {
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
pk.packBigInteger(value);
}
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitInteger(this);
- }
- @Override
- public IntegerValue toValue() {
- return this;
- }
@Override
- public boolean equals(Object o) {
+ public boolean equals(Object o)
+ {
if (o == this) {
return true;
}
@@ -155,18 +226,21 @@ public boolean equals(Object o) {
return false;
}
Value v = (Value) o;
- if (!v.isInteger()) {
+
+ if (!v.isIntegerValue()) {
return false;
}
- IntegerValue iv = v.asInteger();
+ IntegerValue iv = v.asIntegerValue();
return value.equals(iv.toBigInteger());
}
@Override
- public int hashCode() {
+ public int hashCode()
+ {
if (INT_MIN.compareTo(value) <= 0 && value.compareTo(INT_MAX) <= 0) {
return (int) value.longValue();
- } else if (LONG_MIN.compareTo(value) <= 0
+ }
+ else if (LONG_MIN.compareTo(value) <= 0
&& value.compareTo(LONG_MAX) <= 0) {
long v = value.longValue();
return (int) (v ^ (v >>> 32));
@@ -175,7 +249,14 @@ public int hashCode() {
}
@Override
- public String toString() {
+ public String toJson()
+ {
return value.toString();
}
+
+ @Override
+ public String toString()
+ {
+ return toJson();
+ }
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableBinaryValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableBinaryValueImpl.java
new file mode 100644
index 000000000..2d444ae83
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableBinaryValueImpl.java
@@ -0,0 +1,95 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+
+import org.msgpack.core.MessagePacker;
+import org.msgpack.value.ImmutableBinaryValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueType;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * {@code ImmutableBinaryValueImpl} Implements {@code ImmutableBinaryValue} using a {@code byte[]} field.
+ * This implementation caches result of {@code toString()} and {@code asString()} using a private {@code String} field.
+ *
+ * @see org.msgpack.value.StringValue
+ */
+public class ImmutableBinaryValueImpl
+ extends AbstractImmutableRawValue
+ implements ImmutableBinaryValue
+{
+ public ImmutableBinaryValueImpl(byte[] data)
+ {
+ super(data);
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.BINARY;
+ }
+
+ @Override
+ public ImmutableBinaryValue immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableBinaryValue asBinaryValue()
+ {
+ return this;
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ pk.packBinaryHeader(data.length);
+ pk.writePayload(data);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Value)) {
+ return false;
+ }
+ Value v = (Value) o;
+ if (!v.isBinaryValue()) {
+ return false;
+ }
+
+ if (v instanceof ImmutableBinaryValueImpl) {
+ ImmutableBinaryValueImpl bv = (ImmutableBinaryValueImpl) v;
+ return Arrays.equals(data, bv.data);
+ }
+ else {
+ return Arrays.equals(data, v.asBinaryValue().asByteArray());
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Arrays.hashCode(data);
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableBooleanValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableBooleanValueImpl.java
new file mode 100644
index 000000000..535e91c61
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableBooleanValueImpl.java
@@ -0,0 +1,110 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+
+import org.msgpack.core.MessagePacker;
+import org.msgpack.value.ImmutableBooleanValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueType;
+
+import java.io.IOException;
+
+/**
+ * {@code ImmutableBooleanValueImpl} Implements {@code ImmutableBooleanValue} using a {@code boolean} field.
+ *
+ * This class is a singleton. {@code ImmutableBooleanValueImpl.trueInstance()} and {@code ImmutableBooleanValueImpl.falseInstance()} are the only instances of this class.
+ *
+ * @see org.msgpack.value.BooleanValue
+ */
+public class ImmutableBooleanValueImpl
+ extends AbstractImmutableValue
+ implements ImmutableBooleanValue
+{
+ public static final ImmutableBooleanValue TRUE = new ImmutableBooleanValueImpl(true);
+ public static final ImmutableBooleanValue FALSE = new ImmutableBooleanValueImpl(false);
+
+ private final boolean value;
+
+ private ImmutableBooleanValueImpl(boolean value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.BOOLEAN;
+ }
+
+ @Override
+ public ImmutableBooleanValue immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public boolean getBoolean()
+ {
+ return value;
+ }
+
+ @Override
+ public void writeTo(MessagePacker packer)
+ throws IOException
+ {
+ packer.packBoolean(value);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Value)) {
+ return false;
+ }
+ Value v = (Value) o;
+
+ if (!v.isBooleanValue()) {
+ return false;
+ }
+ return value == v.asBooleanValue().getBoolean();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ if (value) {
+ return 1231;
+ }
+ else {
+ return 1237;
+ }
+ }
+
+ @Override
+ public String toJson()
+ {
+ return Boolean.toString(value);
+ }
+
+ @Override
+ public String toString()
+ {
+ return toJson();
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableDoubleValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableDoubleValueImpl.java
new file mode 100644
index 000000000..2aae1633a
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableDoubleValueImpl.java
@@ -0,0 +1,144 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+
+import org.msgpack.core.MessagePacker;
+import org.msgpack.value.ImmutableFloatValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueType;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * {@code ImmutableDoubleValueImpl} Implements {@code ImmutableFloatValue} using a {@code double} field.
+ *
+ * @see org.msgpack.value.FloatValue
+ */
+public class ImmutableDoubleValueImpl
+ extends AbstractImmutableValue
+ implements ImmutableFloatValue
+{
+ private final double value;
+
+ public ImmutableDoubleValueImpl(double value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.FLOAT;
+ }
+
+ @Override
+ public ImmutableDoubleValueImpl immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public byte toByte()
+ {
+ return (byte) value;
+ }
+
+ @Override
+ public short toShort()
+ {
+ return (short) value;
+ }
+
+ @Override
+ public int toInt()
+ {
+ return (int) value;
+ }
+
+ @Override
+ public long toLong()
+ {
+ return (long) value;
+ }
+
+ @Override
+ public BigInteger toBigInteger()
+ {
+ return new BigDecimal(value).toBigInteger();
+ }
+
+ @Override
+ public float toFloat()
+ {
+ return (float) value;
+ }
+
+ @Override
+ public double toDouble()
+ {
+ return value;
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ pk.packDouble(value);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Value)) {
+ return false;
+ }
+ Value v = (Value) o;
+
+ if (!v.isFloatValue()) {
+ return false;
+ }
+ return value == v.asFloatValue().toDouble();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ long v = Double.doubleToLongBits(value);
+ return (int) (v ^ (v >>> 32));
+ }
+
+ @Override
+ public String toJson()
+ {
+ if (Double.isNaN(value) || Double.isInfinite(value)) {
+ return "null";
+ }
+ else {
+ return Double.toString(value);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return Double.toString(value);
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableExtensionValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableExtensionValueImpl.java
new file mode 100644
index 000000000..eb14cc767
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableExtensionValueImpl.java
@@ -0,0 +1,138 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+
+import org.msgpack.core.MessagePacker;
+import org.msgpack.value.ExtensionValue;
+import org.msgpack.value.ImmutableExtensionValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueType;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * {@code ImmutableExtensionValueImpl} Implements {@code ImmutableExtensionValue} using a {@code byte} and a {@code byte[]} fields.
+ *
+ * @see ExtensionValue
+ */
+public class ImmutableExtensionValueImpl
+ extends AbstractImmutableValue
+ implements ImmutableExtensionValue
+{
+ private final byte type;
+ private final byte[] data;
+
+ public ImmutableExtensionValueImpl(byte type, byte[] data)
+ {
+ this.type = type;
+ this.data = data;
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.EXTENSION;
+ }
+
+ @Override
+ public ImmutableExtensionValue immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableExtensionValue asExtensionValue()
+ {
+ return this;
+ }
+
+ @Override
+ public byte getType()
+ {
+ return type;
+ }
+
+ @Override
+ public byte[] getData()
+ {
+ return data;
+ }
+
+ @Override
+ public void writeTo(MessagePacker packer)
+ throws IOException
+ {
+ packer.packExtensionTypeHeader(type, data.length);
+ packer.writePayload(data);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Value)) {
+ return false;
+ }
+ Value v = (Value) o;
+
+ if (!v.isExtensionValue()) {
+ return false;
+ }
+ ExtensionValue ev = v.asExtensionValue();
+ return type == ev.getType() && Arrays.equals(data, ev.getData());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 31 + type;
+ for (byte e : data) {
+ hash = 31 * hash + e;
+ }
+ return hash;
+ }
+
+ @Override
+ public String toJson()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ sb.append(Byte.toString(type));
+ sb.append(",\"");
+ for (byte e : data) {
+ sb.append(Integer.toString((int) e, 16));
+ }
+ sb.append("\"]");
+ return sb.toString();
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append('(');
+ sb.append(Byte.toString(type));
+ sb.append(",0x");
+ for (byte e : data) {
+ sb.append(Integer.toString((int) e, 16));
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/LongValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableLongValueImpl.java
similarity index 50%
rename from msgpack-core/src/main/java/org/msgpack/value/impl/LongValueImpl.java
rename to msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableLongValueImpl.java
index 57346fe11..872b97af0 100644
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/LongValueImpl.java
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableLongValueImpl.java
@@ -1,23 +1,45 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+import org.msgpack.core.MessageFormat;
import org.msgpack.core.MessageIntegerOverflowException;
import org.msgpack.core.MessagePacker;
-import org.msgpack.value.ValueType;
+import org.msgpack.value.ImmutableIntegerValue;
+import org.msgpack.value.ImmutableNumberValue;
import org.msgpack.value.IntegerValue;
import org.msgpack.value.Value;
-import org.msgpack.value.ValueVisitor;
+import org.msgpack.value.ValueType;
import java.io.IOException;
import java.math.BigInteger;
/**
-* Created on 5/30/14.
-*/
-public class LongValueImpl extends AbstractValue implements IntegerValue {
-
+ * {@code ImmutableLongValueImpl} Implements {@code ImmutableIntegerValue} using a {@code long} field.
+ *
+ * @see org.msgpack.value.IntegerValue
+ */
+public class ImmutableLongValueImpl
+ extends AbstractImmutableValue
+ implements ImmutableIntegerValue
+{
private final long value;
- public LongValueImpl(long value) {
+ public ImmutableLongValueImpl(long value)
+ {
this.value = value;
}
@@ -29,123 +51,150 @@ public LongValueImpl(long value) {
private static final long INT_MAX = (long) Integer.MAX_VALUE;
@Override
- public ValueType getValueType() {
+ public ValueType getValueType()
+ {
return ValueType.INTEGER;
}
@Override
- public IntegerValue asInteger() {
+ public ImmutableIntegerValue immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableNumberValue asNumberValue()
+ {
return this;
}
@Override
- public byte toByte() {
+ public ImmutableIntegerValue asIntegerValue()
+ {
+ return this;
+ }
+
+ @Override
+ public byte toByte()
+ {
return (byte) value;
}
@Override
- public short toShort() {
+ public short toShort()
+ {
return (short) value;
}
@Override
- public int toInt() {
+ public int toInt()
+ {
return (int) value;
}
@Override
- public long toLong() {
+ public long toLong()
+ {
return value;
}
@Override
- public BigInteger toBigInteger() {
+ public BigInteger toBigInteger()
+ {
return BigInteger.valueOf(value);
}
@Override
- public float toFloat() {
+ public float toFloat()
+ {
return (float) value;
}
@Override
- public double toDouble() {
+ public double toDouble()
+ {
return (double) value;
}
@Override
- public byte asByte() throws MessageIntegerOverflowException {
- if (!isValidByte()) {
- throw new MessageIntegerOverflowException(value);
- }
- return (byte) value;
+ public boolean isInByteRange()
+ {
+ return BYTE_MIN <= value && value <= BYTE_MAX;
}
@Override
- public short asShort() throws MessageIntegerOverflowException {
- if (!isValidShort()) {
- throw new MessageIntegerOverflowException(value);
- }
- return (short) value;
+ public boolean isInShortRange()
+ {
+ return SHORT_MIN <= value && value <= SHORT_MAX;
}
@Override
- public int asInt() throws MessageIntegerOverflowException {
- if (!isValidInt()) {
- throw new MessageIntegerOverflowException(value);
- }
- return (int) value;
+ public boolean isInIntRange()
+ {
+ return INT_MIN <= value && value <= INT_MAX;
}
@Override
- public long asLong() throws MessageIntegerOverflowException {
- return value;
+ public boolean isInLongRange()
+ {
+ return true;
}
@Override
- public BigInteger asBigInteger() throws MessageIntegerOverflowException {
- return BigInteger.valueOf(value);
+ public MessageFormat mostSuccinctMessageFormat()
+ {
+ return ImmutableBigIntegerValueImpl.mostSuccinctMessageFormat(this);
}
@Override
- public boolean isValidByte() {
- return BYTE_MIN <= value && value <= BYTE_MAX;
+ public byte asByte()
+ {
+ if (!isInByteRange()) {
+ throw new MessageIntegerOverflowException(value);
+ }
+ return (byte) value;
}
@Override
- public boolean isValidShort() {
- return SHORT_MIN <= value && value <= SHORT_MAX;
+ public short asShort()
+ {
+ if (!isInShortRange()) {
+ throw new MessageIntegerOverflowException(value);
+ }
+ return (short) value;
}
@Override
- public boolean isValidInt() {
- return INT_MIN <= value && value <= INT_MAX;
+ public int asInt()
+ {
+ if (!isInIntRange()) {
+ throw new MessageIntegerOverflowException(value);
+ }
+ return (int) value;
}
@Override
- public boolean isValidLong() {
- return true;
+ public long asLong()
+ {
+ return value;
}
+
@Override
- public boolean isWhole() {
- return true;
+ public BigInteger asBigInteger()
+ {
+ return BigInteger.valueOf((long) value);
}
@Override
- public void writeTo(MessagePacker pk) throws IOException {
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
pk.packLong(value);
}
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitInteger(this);
- }
- @Override
- public IntegerValue toValue() {
- return this;
- }
@Override
- public boolean equals(Object o) {
+ public boolean equals(Object o)
+ {
if (o == this) {
return true;
}
@@ -153,27 +202,37 @@ public boolean equals(Object o) {
return false;
}
Value v = (Value) o;
- if (!v.isInteger()) {
+ if (!v.isIntegerValue()) {
return false;
}
- IntegerValue iv = v.asInteger();
- if (!iv.isValidLong()) {
+
+ IntegerValue iv = v.asIntegerValue();
+ if (!iv.isInLongRange()) {
return false;
}
return value == iv.toLong();
}
@Override
- public int hashCode() {
+ public int hashCode()
+ {
if (INT_MIN <= value && value <= INT_MAX) {
return (int) value;
- } else {
+ }
+ else {
return (int) (value ^ (value >>> 32));
}
}
@Override
- public String toString() {
+ public String toJson()
+ {
return Long.toString(value);
}
+
+ @Override
+ public String toString()
+ {
+ return toJson();
+ }
}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableMapValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableMapValueImpl.java
new file mode 100644
index 000000000..dc55d783f
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableMapValueImpl.java
@@ -0,0 +1,373 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+
+import org.msgpack.core.MessagePacker;
+import org.msgpack.value.ImmutableMapValue;
+import org.msgpack.value.MapValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueType;
+
+import java.io.IOException;
+import java.util.AbstractCollection;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * {@code ImmutableMapValueImpl} Implements {@code ImmutableMapValue} using a {@code Value[]} field.
+ *
+ * @see org.msgpack.value.MapValue
+ */
+public class ImmutableMapValueImpl
+ extends AbstractImmutableValue
+ implements ImmutableMapValue
+{
+ private static final ImmutableMapValueImpl EMPTY = new ImmutableMapValueImpl(new Value[0]);
+
+ public static ImmutableMapValue empty()
+ {
+ return EMPTY;
+ }
+
+ private final Value[] kvs;
+
+ public ImmutableMapValueImpl(Value[] kvs)
+ {
+ this.kvs = kvs;
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.MAP;
+ }
+
+ @Override
+ public ImmutableMapValue immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableMapValue asMapValue()
+ {
+ return this;
+ }
+
+ @Override
+ public Value[] getKeyValueArray()
+ {
+ return Arrays.copyOf(kvs, kvs.length);
+ }
+
+ @Override
+ public int size()
+ {
+ return kvs.length / 2;
+ }
+
+ @Override
+ public Set keySet()
+ {
+ return new KeySet(kvs);
+ }
+
+ @Override
+ public Set> entrySet()
+ {
+ return new EntrySet(kvs);
+ }
+
+ @Override
+ public Collection values()
+ {
+ return new ValueCollection(kvs);
+ }
+
+ @Override
+ public Map map()
+ {
+ return new ImmutableMapValueMap(kvs);
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ pk.packMapHeader(kvs.length / 2);
+ for (int i = 0; i < kvs.length; i++) {
+ kvs[i].writeTo(pk);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Value)) {
+ return false;
+ }
+ Value v = (Value) o;
+
+ if (!v.isMapValue()) {
+ return false;
+ }
+ MapValue mv = v.asMapValue();
+ return map().equals(mv.map());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int h = 0;
+ for (int i = 0; i < kvs.length; i += 2) {
+ h += kvs[i].hashCode() ^ kvs[i + 1].hashCode();
+ }
+ return h;
+ }
+
+ @Override
+ public String toJson()
+ {
+ if (kvs.length == 0) {
+ return "{}";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ appendJsonKey(sb, kvs[0]);
+ sb.append(":");
+ sb.append(kvs[1].toJson());
+ for (int i = 2; i < kvs.length; i += 2) {
+ sb.append(",");
+ appendJsonKey(sb, kvs[i]);
+ sb.append(":");
+ sb.append(kvs[i + 1].toJson());
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private static void appendJsonKey(StringBuilder sb, Value key)
+ {
+ if (key.isRawValue()) {
+ sb.append(key.toJson());
+ }
+ else {
+ ImmutableStringValueImpl.appendJsonString(sb, key.toString());
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ if (kvs.length == 0) {
+ return "{}";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ appendString(sb, kvs[0]);
+ sb.append(":");
+ appendString(sb, kvs[1]);
+ for (int i = 2; i < kvs.length; i += 2) {
+ sb.append(",");
+ appendString(sb, kvs[i]);
+ sb.append(":");
+ appendString(sb, kvs[i + 1]);
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private static void appendString(StringBuilder sb, Value value)
+ {
+ if (value.isRawValue()) {
+ sb.append(value.toJson());
+ }
+ else {
+ sb.append(value.toString());
+ }
+ }
+
+ private static class ImmutableMapValueMap
+ extends AbstractMap
+ {
+ private final Value[] kvs;
+
+ public ImmutableMapValueMap(Value[] kvs)
+ {
+ this.kvs = kvs;
+ }
+
+ @Override
+ public Set> entrySet()
+ {
+ return new EntrySet(kvs);
+ }
+ }
+
+ private static class EntrySet
+ extends AbstractSet>
+ {
+ private final Value[] kvs;
+
+ EntrySet(Value[] kvs)
+ {
+ this.kvs = kvs;
+ }
+
+ @Override
+ public int size()
+ {
+ return kvs.length / 2;
+ }
+
+ @Override
+ public Iterator> iterator()
+ {
+ return new EntrySetIterator(kvs);
+ }
+ }
+
+ private static class EntrySetIterator
+ implements Iterator>
+ {
+ private final Value[] kvs;
+ private int index;
+
+ EntrySetIterator(Value[] kvs)
+ {
+ this.kvs = kvs;
+ this.index = 0;
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return index < kvs.length;
+ }
+
+ @Override
+ public Map.Entry next()
+ {
+ if (index >= kvs.length) {
+ throw new NoSuchElementException(); // TODO message
+ }
+
+ Value key = kvs[index];
+ Value value = kvs[index + 1];
+ Map.Entry pair = new AbstractMap.SimpleImmutableEntry(key, value);
+
+ index += 2;
+ return pair;
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException(); // TODO message
+ }
+ }
+
+ private static class KeySet
+ extends AbstractSet
+ {
+ private Value[] kvs;
+
+ KeySet(Value[] kvs)
+ {
+ this.kvs = kvs;
+ }
+
+ @Override
+ public int size()
+ {
+ return kvs.length / 2;
+ }
+
+ @Override
+ public Iterator iterator()
+ {
+ return new EntryIterator(kvs, 0);
+ }
+ }
+
+ private static class ValueCollection
+ extends AbstractCollection
+ {
+ private Value[] kvs;
+
+ ValueCollection(Value[] kvs)
+ {
+ this.kvs = kvs;
+ }
+
+ @Override
+ public int size()
+ {
+ return kvs.length / 2;
+ }
+
+ @Override
+ public Iterator iterator()
+ {
+ return new EntryIterator(kvs, 1);
+ }
+ }
+
+ private static class EntryIterator
+ implements Iterator
+ {
+ private Value[] kvs;
+ private int index;
+
+ public EntryIterator(Value[] kvs, int offset)
+ {
+ this.kvs = kvs;
+ this.index = offset;
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return index < kvs.length;
+ }
+
+ @Override
+ public Value next()
+ {
+ int i = index;
+ if (i >= kvs.length) {
+ throw new NoSuchElementException();
+ }
+ index = i + 2;
+ return kvs[i];
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableNilValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableNilValueImpl.java
new file mode 100644
index 000000000..7077d115e
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableNilValueImpl.java
@@ -0,0 +1,101 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+
+import org.msgpack.core.MessagePacker;
+import org.msgpack.value.ImmutableNilValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueType;
+
+import java.io.IOException;
+
+/**
+ * {@code ImmutableNilValueImpl} Implements {@code ImmutableNilValue}.
+ *
+ * This class is a singleton. {@code ImmutableNilValueImpl.get()} is the only instances of this class.
+ *
+ * @see org.msgpack.value.NilValue
+ */
+public class ImmutableNilValueImpl
+ extends AbstractImmutableValue
+ implements ImmutableNilValue
+{
+ private static ImmutableNilValue instance = new ImmutableNilValueImpl();
+
+ public static ImmutableNilValue get()
+ {
+ return instance;
+ }
+
+ private ImmutableNilValueImpl()
+ {
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.NIL;
+ }
+
+ @Override
+ public ImmutableNilValue immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableNilValue asNilValue()
+ {
+ return this;
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ pk.packNil();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Value)) {
+ return false;
+ }
+ return ((Value) o).isNilValue();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return 0;
+ }
+
+ @Override
+ public String toString()
+ {
+ return toJson();
+ }
+
+ @Override
+ public String toJson()
+ {
+ return "null";
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableStringValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableStringValueImpl.java
new file mode 100644
index 000000000..dbdb57195
--- /dev/null
+++ b/msgpack-core/src/main/java/org/msgpack/value/impl/ImmutableStringValueImpl.java
@@ -0,0 +1,100 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value.impl;
+
+import org.msgpack.core.MessagePacker;
+import org.msgpack.value.ImmutableStringValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueType;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * {@code ImmutableStringValueImpl} Implements {@code ImmutableStringValue} using a {@code byte[]} field.
+ * This implementation caches result of {@code toString()} and {@code asString()} using a private {@code String} field.
+ *
+ * @see org.msgpack.value.StringValue
+ */
+public class ImmutableStringValueImpl
+ extends AbstractImmutableRawValue
+ implements ImmutableStringValue
+{
+ public ImmutableStringValueImpl(byte[] data)
+ {
+ super(data);
+ }
+
+ public ImmutableStringValueImpl(String string)
+ {
+ super(string);
+ }
+
+ @Override
+ public ValueType getValueType()
+ {
+ return ValueType.STRING;
+ }
+
+ @Override
+ public ImmutableStringValue immutableValue()
+ {
+ return this;
+ }
+
+ @Override
+ public ImmutableStringValue asStringValue()
+ {
+ return this;
+ }
+
+ @Override
+ public void writeTo(MessagePacker pk)
+ throws IOException
+ {
+ pk.packRawStringHeader(data.length);
+ pk.writePayload(data);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Value)) {
+ return false;
+ }
+ Value v = (Value) o;
+ if (!v.isStringValue()) {
+ return false;
+ }
+
+ if (v instanceof ImmutableStringValueImpl) {
+ ImmutableStringValueImpl bv = (ImmutableStringValueImpl) v;
+ return Arrays.equals(data, bv.data);
+ }
+ else {
+ return Arrays.equals(data, v.asStringValue().asByteArray());
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Arrays.hashCode(data);
+ }
+}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/IntegerValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/IntegerValueImpl.java
deleted file mode 100644
index 758fde939..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/IntegerValueImpl.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessageIntegerOverflowException;
-import org.msgpack.core.MessageOverflowException;
-import org.msgpack.core.MessagePacker;
-import org.msgpack.value.*;
-
-import java.io.IOException;
-import java.math.BigInteger;
-
-/**
-* Created on 5/30/14.
-*/
-public class IntegerValueImpl extends AbstractValue implements IntegerValue {
-
- private final int value;
-
- public IntegerValueImpl(int value) {
- this.value = value;
- }
-
- private static int BYTE_MIN = (int) Byte.MIN_VALUE;
- private static int BYTE_MAX = (int) Byte.MAX_VALUE;
- private static int SHORT_MIN = (int) Short.MIN_VALUE;
- private static int SHORT_MAX = (int) Short.MAX_VALUE;
-
- @Override
- public ValueType getValueType() {
- return ValueType.INTEGER;
- }
-
- @Override
- public IntegerValue asInteger() {
- return this;
- }
-
- @Override
- public byte toByte() {
- return (byte) value;
- }
-
- @Override
- public short toShort() {
- return (short) value;
- }
-
- @Override
- public int toInt() {
- return value;
- }
-
- @Override
- public long toLong() {
- return value;
- }
-
- @Override
- public BigInteger toBigInteger() {
- return BigInteger.valueOf((long) value);
- }
-
- @Override
- public float toFloat() {
- return (float) value;
- }
-
- @Override
- public double toDouble() {
- return (double) value;
- }
-
- @Override
- public byte asByte() throws MessageOverflowException {
- if (!isValidByte()) {
- throw new MessageIntegerOverflowException(value);
- }
- return (byte) value;
- }
-
- @Override
- public short asShort() throws MessageOverflowException {
- if (!isValidShort()) {
- throw new MessageIntegerOverflowException(value);
- }
- return (short) value;
- }
-
- @Override
- public int asInt() throws MessageOverflowException {
- return value;
- }
-
- @Override
- public long asLong() throws MessageOverflowException {
- return value;
- }
-
- @Override
- public BigInteger asBigInteger() throws MessageOverflowException {
- return BigInteger.valueOf((long) value);
- }
-
- @Override
- public boolean isValidByte() {
- return BYTE_MIN <= value && value <= BYTE_MAX;
- }
-
- @Override
- public boolean isValidShort() {
- return SHORT_MIN <= value && value <= SHORT_MAX;
- }
-
- @Override
- public boolean isValidInt() {
- return true;
- }
-
- @Override
- public boolean isValidLong() {
- return true;
- }
-
- @Override
- public boolean isWhole() {
- return true;
- }
-
- @Override
- public void writeTo(MessagePacker pk) throws IOException {
- pk.packInt(value);
- }
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitInteger(this);
- }
- @Override
- public IntegerValue toValue() {
- return this;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof Value)) {
- return false;
- }
- Value v = (Value) o;
- if (!v.isInteger()) {
- return false;
- }
- IntegerValue iv = v.asInteger();
- if (!iv.isValidInt()) {
- return false;
- }
- return iv.toInt() == value;
- }
-
- @Override
- public int hashCode() {
- return value;
- }
-
- @Override
- public String toString() {
- return Integer.toString(value);
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/MapCursorImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/MapCursorImpl.java
deleted file mode 100644
index fbd74e766..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/MapCursorImpl.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.*;
-import org.msgpack.value.*;
-import org.msgpack.value.holder.ValueHolder;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-
-import static org.msgpack.core.MessagePackException.UNSUPPORTED;
-
-/**
- * Created on 6/16/14.
- */
-public class MapCursorImpl extends AbstractValueRef implements MapCursor {
-
- private final ValueHolder valueHolder;
- private MessageUnpacker unpacker;
- private int cursor = 0;
- private int mapSize;
-
- public MapCursorImpl(ValueHolder valueHolder) {
- this.valueHolder = valueHolder;
- }
-
- public void reset(MessageUnpacker unpacker) throws IOException {
- this.unpacker = unpacker;
- cursor = 0;
- this.mapSize = unpacker.unpackMapHeader();
- }
-
- @Override
- public int size() {
- return mapSize;
- }
- @Override
- public boolean hasNext() {
- try {
- return cursor < (mapSize * 2) && unpacker.hasNext();
- }
- catch(IOException e) {
- return false;
- }
- }
-
- @Override
- public ValueRef nextKeyOrValue() {
- try {
- MessageFormat f = unpacker.unpackValue(valueHolder);
- cursor++;
- return valueHolder.getRef();
- }
- catch(IOException e) {
- throw new MessageFormatException(e);
- }
- }
-
- @Override
- public void skipKeyOrValue() {
- try {
- unpacker.skipValue();
- }
- catch(IOException e) {
- throw new MessageFormatException(e);
- }
- }
-
- @Override
- public void skipAll() {
- while(hasNext()) {
- skipKeyOrValue();
- }
- }
-
- private void ensureNotTraversed() {
- if(cursor != 0)
- throw UNSUPPORTED("MapCursor is already traversed");
- }
-
-
- @Override
- public MapCursor getMapCursor() throws MessageTypeException {
- return this;
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.MAP;
- }
-
- @Override
- public void writeTo(MessagePacker packer) throws IOException {
- ensureNotTraversed();
- packer.packMapHeader(mapSize);
- while(hasNext()) {
- packer.packValue(nextKeyOrValue().toValue());
- }
- }
-
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitMap(this.toValue());
- }
-
- @Override
- public MapValue toValue() {
- ensureNotTraversed();
- Value[] keyValueArray = new Value[mapSize * 2];
- int i = 0;
- while(hasNext()) {
- keyValueArray[i++] = nextKeyOrValue().toValue();
- }
- return ValueFactory.newMap(keyValueArray);
- }
-
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/MapValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/MapValueImpl.java
deleted file mode 100644
index 6d6b523ba..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/MapValueImpl.java
+++ /dev/null
@@ -1,277 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessagePacker;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.*;
-
-import java.io.IOException;
-import java.util.*;
-
-/**
-* Created on 5/30/14.
-*/
-public class MapValueImpl extends AbstractValue implements MapValue {
- private static MapValueImpl EMPTY = new MapValueImpl(new Value[0]);
-
- public static MapValue empty() {
- return EMPTY;
- }
-
- private final Value[] array;
- private transient int cursor = 0;
-
- public MapValueImpl(Value[] array) {
- this.array = array;
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.MAP;
- }
-
- @Override
- public Value[] toKeyValueSeq() {
- return Arrays.copyOf(array, array.length);
- }
- @Override
- public int size() {
- return array.length / 2;
- }
-
- @Override
- public boolean hasNext() {
- return cursor < array.length;
- }
- @Override
- public ValueRef nextKeyOrValue() {
- return array[cursor++];
- }
-
- @Override
- public void skipKeyOrValue() {
- cursor++;
- }
- @Override
- public void skipAll() {
- while(hasNext()) {
- skipKeyOrValue();
- }
- }
-
-
- private class MapImpl extends AbstractMap {
-
- private class EntrySet extends AbstractSet> {
-
- @Override
- public int size() {
- return array.length / 2;
- }
-
- @Override
- public Iterator> iterator() {
- return new EntrySetIterator();
- }
- }
-
- private class EntrySetIterator implements
- Iterator> {
- private int pos = 0;
-
- @Override
- public boolean hasNext() {
- return pos < array.length;
- }
-
- @Override
- public Entry next() {
- if (pos >= array.length) {
- throw new NoSuchElementException();
- }
-
- Value key = array[pos];
- Value value = array[pos + 1];
- Entry pair = new SimpleImmutableEntry(key, value);
-
- pos += 2;
- return pair;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
-
- private class KeySet extends AbstractSet {
- @Override
- public int size() {
- return array.length / 2;
- }
-
- @Override
- public Iterator iterator() {
- return new ValueIterator(0);
- }
- }
-
- private class ValueCollection extends AbstractCollection {
-
- @Override
- public int size() {
- return array.length / 2;
- }
-
- @Override
- public Iterator iterator() {
- return new ValueIterator(1);
- }
- }
-
- private class ValueIterator implements Iterator {
- private int pos;
-
- ValueIterator(int offset) {
- this.pos = offset;
- }
-
- @Override
- public boolean hasNext() {
- return pos < array.length;
- }
-
- @Override
- public Value next() {
- if (pos >= array.length) {
- throw new NoSuchElementException();
- }
- Value v = array[pos];
- pos += 2;
- return v;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
-
- @Override
- public Set> entrySet() {
- return new EntrySet();
- }
-
- @Override
- public Set keySet() {
- return new KeySet();
- }
-
- @Override
- public Collection values() {
- return new ValueCollection();
- }
-
-
- @Override
- public Value get(Object key) {
- for (int i = array.length - 2; i >= 0; i -= 2) {
- if (array[i].equals(key)) {
- return array[i + 1];
- }
- }
- return null;
- }
- }
-
- @Override
- public Map toMap() {
- return new MapImpl();
- }
-
- @Override
- public void writeTo(MessagePacker pk) throws IOException {
- pk.packMapHeader(array.length / 2);
- for (int i = 0; i < array.length; i++) {
- array[i].writeTo(pk);
- }
- }
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitMap(this);
- }
-
- @Override
- public MapValue toValue() {
- return ValueFactory.newMap(array);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof Value)) {
- return false;
- }
- Value v = (Value) o;
- if (!v.isMap()) {
- return false;
- }
-
- Map mv = v.asMapValue().toMap();
- if (mv.size() != array.length / 2) {
- return false;
- }
-
- try {
- for (int i = 0; i < array.length; i += 2) {
- Value key = array[i];
- Value value = array[i + 1];
- if (!value.equals(mv.get(key))) {
- return false;
- }
- }
- } catch (ClassCastException ex) {
- return false;
- } catch (NullPointerException ex) {
- return false;
- }
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int h = 0;
- for (int i = 0; i < array.length; i += 2) {
- h += array[i].hashCode() ^ array[i + 1].hashCode();
- }
- return h;
- }
-
- @Override
- public String toString() {
- return toString(new StringBuilder()).toString();
- }
-
- private StringBuilder toString(StringBuilder sb) {
- if (array.length == 0) {
- return sb.append("{}");
- }
- sb.append("{");
- sb.append(array[0]);
- sb.append(":");
- sb.append(array[1]);
- for (int i = 2; i < array.length; i += 2) {
- sb.append(",");
- sb.append(array[i].toString());
- sb.append(":");
- sb.append(array[i + 1].toString());
- }
- sb.append("}");
- return sb;
- }
-
-
-
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/NilValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/NilValueImpl.java
deleted file mode 100644
index 3fea78364..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/NilValueImpl.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessagePacker;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.NilValue;
-import org.msgpack.value.Value;
-import org.msgpack.value.ValueVisitor;
-
-import java.io.IOException;
-
-/**
-* Created on 5/30/14.
-*/
-public class NilValueImpl extends AbstractValue implements NilValue {
-
- private static NilValue instance = new NilValueImpl();
-
- public static NilValue getInstance() {
- return instance;
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.NIL;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof Value)) {
- return false;
- }
- return ((Value) o).isNil();
- }
-
- @Override
- public int hashCode() {
- return 0;
- }
-
- @Override
- public String toString() {
- return "null";
- }
-
- @Override
- public void writeTo(MessagePacker packer) throws IOException {
- packer.packNil();
- }
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitNil();
- }
- @Override
- public NilValue toValue() {
- return instance;
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/RawStringValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/RawStringValueImpl.java
deleted file mode 100644
index a4b0e43bd..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/RawStringValueImpl.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessagePacker;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.*;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
-* Created on 5/30/14.
-*/
-public class RawStringValueImpl extends RawValueImpl implements StringValue {
-
- public RawStringValueImpl(ByteBuffer byteBuffer) {
- super(byteBuffer);
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.STRING;
- }
-
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitString(this);
- }
-
- @Override
- public String toString() {
- return super.toString();
- }
-
- @Override
- public void writeTo(MessagePacker pk) throws IOException {
- pk.packRawStringHeader(byteBuffer.remaining());
- pk.writePayload(byteBuffer);
- }
-
- @Override
- public StringValue toValue() {
- return this;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof Value)) {
- return false;
- }
- Value v = (Value) o;
- if (!v.isString()) {
- return false;
- }
- StringValue sv = v.asString();
- return sv.toByteBuffer().equals(byteBuffer);
- }
-
- @Override
- public int hashCode() {
- return byteBuffer.hashCode();
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/RawValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/RawValueImpl.java
deleted file mode 100644
index b69677503..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/RawValueImpl.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessagePacker;
-import org.msgpack.core.MessageStringCodingException;
-import org.msgpack.core.buffer.MessageBuffer;
-import org.msgpack.value.BinaryValue;
-import org.msgpack.value.RawValue;
-import org.msgpack.value.Value;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.*;
-
-/**
-* Created on 5/30/14.
-*/
-public abstract class RawValueImpl extends AbstractValue implements RawValue {
-
- protected final ByteBuffer byteBuffer;
- private transient String decodedStringCache;
- private transient MessageStringCodingException codingException;
-
- public RawValueImpl(ByteBuffer byteBuffer) {
- this.byteBuffer = byteBuffer.slice();
- }
-
- @Override
- public byte[] toByteArray() {
- byte[] byteArray = new byte[byteBuffer.remaining()];
- byteBuffer.slice().get(byteArray);
- return byteArray;
- }
-
- @Override
- public RawValue toValue() {
- return this;
- }
-
- @Override
- public ByteBuffer toByteBuffer() {
- return byteBuffer.asReadOnlyBuffer();
- }
-
- @Override
- public MessageBuffer toMessageBuffer() {
- return MessageBuffer.wrap(byteBuffer);
- }
-
- @Override
- public String toString() {
- if (decodedStringCache == null) {
- decodeString();
- }
- if (codingException != null) {
- throw codingException;
- }
- return decodedStringCache;
- }
-
-
- private synchronized void decodeString() {
- if (decodedStringCache != null) {
- return;
- }
- try {
- CharsetDecoder reportDecoder = Charset.forName("UTF-8").newDecoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
- decodedStringCache = reportDecoder.decode(byteBuffer.asReadOnlyBuffer()).toString();
- } catch (UnsupportedCharsetException neverThrown) {
- throw new AssertionError(neverThrown);
- } catch (CharacterCodingException ex) {
- codingException = new MessageStringCodingException(ex);
- try {
- CharsetDecoder replaceDecoder = Charset.forName("UTF-8").newDecoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
- decodedStringCache = replaceDecoder.decode(byteBuffer.asReadOnlyBuffer()).toString();
- } catch (UnsupportedCharsetException neverThrown) {
- throw new AssertionError(neverThrown);
- } catch (CharacterCodingException neverThrown) {
- throw new AssertionError(neverThrown);
- }
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof Value)) {
- return false;
- }
- Value v = (Value) o;
- if (!v.isBinary()) {
- return false;
- }
- BinaryValue bv = v.asBinary();
- return bv.toByteBuffer().equals(byteBuffer);
- }
-
- @Override
- public int hashCode() {
- return byteBuffer.hashCode();
- }
-
- @Override
- public void writeTo(MessagePacker packer) throws IOException {
- packer.packBinaryHeader(byteBuffer.remaining());
- packer.writePayload(byteBuffer);
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/StringValueImpl.java b/msgpack-core/src/main/java/org/msgpack/value/impl/StringValueImpl.java
deleted file mode 100644
index 0b9888648..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/StringValueImpl.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.msgpack.value.impl;
-
-import org.msgpack.core.MessagePack;
-import org.msgpack.core.MessagePacker;
-import org.msgpack.core.MessageStringCodingException;
-import org.msgpack.core.buffer.MessageBuffer;
-import org.msgpack.value.ValueType;
-import org.msgpack.value.*;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
-* Created on 5/30/14.
-*/
-public class StringValueImpl extends AbstractValue implements StringValue {
-
- private final String value;
-
- public StringValueImpl(String value) {
- this.value = value;
- }
-
- @Override
- public ValueType getValueType() {
- return ValueType.STRING;
- }
-
- @Override
- public byte[] toByteArray() {
- return value.getBytes(MessagePack.UTF8);
- }
-
- @Override
- public MessageBuffer toMessageBuffer() {
- return MessageBuffer.wrap(toByteArray());
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public ByteBuffer toByteBuffer() {
- return toMessageBuffer().toByteBuffer();
- }
-
- @Override
- public void writeTo(MessagePacker pk) throws IOException {
- pk.packString(value);
- }
- @Override
- public void accept(ValueVisitor visitor) {
- visitor.visitString(this);
- }
- @Override
- public StringValue toValue() {
- return ValueFactory.newString(value);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof Value)) {
- return false;
- }
- Value v = (Value) o;
- if (!v.isString()) {
- return false;
- }
- try {
- return v.asString().toString().equals(value);
- } catch (MessageStringCodingException ex) {
- return false;
- }
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/value/impl/ValueUnion.java b/msgpack-core/src/main/java/org/msgpack/value/impl/ValueUnion.java
deleted file mode 100644
index 0a282ef0d..000000000
--- a/msgpack-core/src/main/java/org/msgpack/value/impl/ValueUnion.java
+++ /dev/null
@@ -1,131 +0,0 @@
-//
-// MessagePack for Java
-//
-// Licensed 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 org.msgpack.value.impl;
-
-import java.util.List;
-import java.util.Map;
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-
-import org.msgpack.value.Value;
-
-/**
- * Union of multiple values so that we can minimize the object initialization cost
- */
-class ValueUnion {
- public static enum Type {
- BOOLEAN,
- LONG,
- BIG_INTEGER,
- DOUBLE,
- BYTE_BUFFER,
- STRING,
- LIST,
- MAP;
- }
-
- private Type type;
-
- private long longValue;
- private double doubleValue;
- private Object objectValue;
-
- public void reset() {
- this.type = null;
- }
-
- public boolean isSet() {
- return type != null;
- }
-
- public Type getType() {
- return type;
- }
-
- public void setBoolean(boolean v) {
- this.type = Type.BOOLEAN;
- this.longValue = (v ? 1L : 0L);
- }
-
- public boolean getBoolean() {
- return longValue != 0L;
- }
-
- public void setLong(long v) {
- this.type = Type.LONG;
- this.longValue = v;
- }
-
- public long getLong() {
- return longValue;
- }
-
- public void setBigInteger(BigInteger v) {
- this.type = Type.BIG_INTEGER;
- this.objectValue = v;
- }
-
- public BigInteger getBigInteger() {
- return (BigInteger) objectValue;
- }
-
- public void setDouble(double v) {
- this.type = Type.DOUBLE;
- this.doubleValue = v;
- }
-
- public double getDouble() {
- return doubleValue;
- }
-
- public void setByteBuffer(ByteBuffer v) {
- this.type = Type.BYTE_BUFFER;
- this.objectValue = v;
- }
-
- public ByteBuffer getByteBuffer() {
- return (ByteBuffer) objectValue;
- }
-
- public void setString(String v) {
- this.type = Type.STRING;
- this.objectValue = v;
- }
-
- public String getString() {
- return (String) objectValue;
- }
-
- public void setList(List v) {
- this.type = Type.LIST;
- this.objectValue = v;
- }
-
- @SuppressWarnings("unchecked")
- public List getList() {
- return (List) objectValue;
- }
-
- public void setMap(Map v) {
- this.type = Type.MAP;
- this.objectValue = v;
- }
-
- @SuppressWarnings("unchecked")
- public Map getMap() {
- return (Map) objectValue;
- }
-}
diff --git a/msgpack-core/src/main/java/org/msgpack/core/example/MessagePackExample.java b/msgpack-core/src/test/java/org/msgpack/core/example/MessagePackExample.java
similarity index 63%
rename from msgpack-core/src/main/java/org/msgpack/core/example/MessagePackExample.java
rename to msgpack-core/src/test/java/org/msgpack/core/example/MessagePackExample.java
index be409e932..e8802a037 100644
--- a/msgpack-core/src/main/java/org/msgpack/core/example/MessagePackExample.java
+++ b/msgpack-core/src/test/java/org/msgpack/core/example/MessagePackExample.java
@@ -15,40 +15,51 @@
//
package org.msgpack.core.example;
-import org.msgpack.core.*;
-import org.msgpack.core.buffer.MessageBuffer;
-import org.msgpack.value.*;
-import org.msgpack.value.holder.FloatHolder;
-import org.msgpack.value.holder.IntegerHolder;
-import org.msgpack.value.holder.ValueHolder;
-
-import java.io.*;
+import org.msgpack.core.MessageFormat;
+import org.msgpack.core.MessagePack;
+import org.msgpack.core.MessagePack.PackerConfig;
+import org.msgpack.core.MessagePack.UnpackerConfig;
+import org.msgpack.core.MessagePacker;
+import org.msgpack.core.MessageUnpacker;
+import org.msgpack.value.ArrayValue;
+import org.msgpack.value.ExtensionValue;
+import org.msgpack.value.FloatValue;
+import org.msgpack.value.IntegerValue;
+import org.msgpack.value.Value;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.nio.charset.CodingErrorAction;
-import java.util.Random;
/**
* This class describes the usage of MessagePack v07
*/
-public class MessagePackExample {
-
+public class MessagePackExample
+{
+ private MessagePackExample()
+ {
+ }
/**
* Basic usage example
+ *
* @throws IOException
*/
- public static void basicUsage() throws IOException {
-
+ public static void basicUsage()
+ throws IOException
+ {
// Serialize with MessagePacker
ByteArrayOutputStream out = new ByteArrayOutputStream();
MessagePacker packer = MessagePack.newDefaultPacker(out);
packer
- .packInt(1)
- .packString("leo")
- .packArrayHeader(2)
- .packString("xxx-xxxx")
- .packString("yyy-yyyy");
+ .packInt(1)
+ .packString("leo")
+ .packArrayHeader(2)
+ .packString("xxx-xxxx")
+ .packString("yyy-yyyy");
packer.close();
// Deserialize with MessageUnpacker
@@ -57,7 +68,7 @@ public static void basicUsage() throws IOException {
String name = unpacker.unpackString(); // "leo"
int numPhones = unpacker.unpackArrayHeader(); // 2
String[] phones = new String[numPhones];
- for(int i=0; i < numPhones; ++i) {
+ for (int i = 0; i < numPhones; ++i) {
phones[i] = unpacker.unpackString(); // phones = {"xxx-xxxx", "yyy-yyyy"}
}
unpacker.close();
@@ -65,10 +76,11 @@ public static void basicUsage() throws IOException {
System.out.println(String.format("id:%d, name:%s, phone:[%s]", id, name, join(phones)));
}
- private static String join(String[] in) {
+ private static String join(String[] in)
+ {
StringBuilder s = new StringBuilder();
- for(int i=0; i 0) {
+ for (int i = 0; i < in.length; ++i) {
+ if (i > 0) {
s.append(", ");
}
s.append(in[i]);
@@ -78,10 +90,12 @@ private static String join(String[] in) {
/**
* Packing various types of data
+ *
* @throws IOException
*/
- public static void packer() throws IOException {
-
+ public static void packer()
+ throws IOException
+ {
// Create a MesagePacker (encoder) instance
ByteArrayOutputStream out = new ByteArrayOutputStream();
MessagePacker packer = MessagePack.newDefaultPacker(out);
@@ -107,9 +121,9 @@ public static void packer() throws IOException {
packer.writePayload(s);
// pack arrays
- int[] arr = new int[]{3, 5, 1, 0, -1, 255};
+ int[] arr = new int[] {3, 5, 1, 0, -1, 255};
packer.packArrayHeader(arr.length);
- for(int v : arr) {
+ for (int v : arr) {
packer.packInt(v);
}
@@ -123,39 +137,32 @@ public static void packer() throws IOException {
packer.packInt(2);
// pack binary data
- byte[] ba = new byte[]{1, 2, 3, 4};
+ byte[] ba = new byte[] {1, 2, 3, 4};
packer.packBinaryHeader(ba.length);
packer.writePayload(ba);
-
// Write ext type data: https://github.com/msgpack/msgpack/blob/master/spec.md#ext-format-family
byte[] extData = "custom data type".getBytes(MessagePack.UTF8);
- packer.packExtendedTypeHeader(1, 10); // type number [0, 127], data byte length
+ packer.packExtensionTypeHeader((byte) 1, 10); // type number [0, 127], data byte length
packer.writePayload(extData);
// Succinct syntax for packing
packer
- .packInt(1)
- .packString("leo")
- .packArrayHeader(2)
- .packString("xxx-xxxx")
- .packString("yyy-yyyy");
-
-
- // [Advanced] write data using ByteBuffer
- ByteBuffer bb = ByteBuffer.wrap(new byte[] {'b', 'i', 'n', 'a', 'r', 'y', 'd', 'a', 't', 'a'});
- packer.packBinaryHeader(bb.remaining());
- packer.writePayload(bb);
-
+ .packInt(1)
+ .packString("leo")
+ .packArrayHeader(2)
+ .packString("xxx-xxxx")
+ .packString("yyy-yyyy");
}
-
/**
* An example of reading and writing MessagePack data
+ *
* @throws IOException
*/
- public static void readAndWriteFile() throws IOException {
-
+ public static void readAndWriteFile()
+ throws IOException
+ {
File tempFile = File.createTempFile("target/tmp", ".txt");
tempFile.deleteOnExit();
@@ -169,99 +176,92 @@ public static void readAndWriteFile() throws IOException {
// Read packed data from a file. No need exists to wrap the file stream with an buffer
MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(new FileInputStream(tempFile));
- while(unpacker.hasNext()) {
+ while (unpacker.hasNext()) {
// [Advanced] You can check the detailed data format with getNextFormat()
// Here is a list of message pack data format: https://github.com/msgpack/msgpack/blob/master/spec.md#overview
MessageFormat format = unpacker.getNextFormat();
// Alternatively you can use ValueHolder to extract a value of any type
// NOTE: Value interface is in a preliminary state, so the following code might change in future releases
- ValueHolder v = new ValueHolder();
- format = unpacker.unpackValue(v);
- switch(format.getValueType()) {
+ Value v = unpacker.unpackValue();
+ switch (v.getValueType()) {
case NIL:
- Value nil = v.get();
- nil.isNil(); // true
+ v.isNilValue(); // true
System.out.println("read nil");
break;
case BOOLEAN:
- boolean b = v.get().asBoolean().toBoolean();
+ boolean b = v.asBooleanValue().getBoolean();
System.out.println("read boolean: " + b);
break;
case INTEGER:
- IntegerHolder ih = v.getIntegerHolder();
- if(ih.isValidInt()) { // int range check [-2^31-1, 2^31-1]
- int i = ih.asInt();
+ IntegerValue iv = v.asIntegerValue();
+ if (iv.isInIntRange()) {
+ int i = iv.toInt();
System.out.println("read int: " + i);
}
- else {
- long l = ih.asLong();
+ else if (iv.isInLongRange()) {
+ long l = iv.toLong();
System.out.println("read long: " + l);
}
+ else {
+ BigInteger i = iv.toBigInteger();
+ System.out.println("read long: " + i);
+ }
break;
case FLOAT:
- FloatHolder fh = v.getFloatHolder();
- float f = fh.toFloat(); // read as float
- double d = fh.toDouble(); // read as double
+ FloatValue fv = v.asFloatValue();
+ float f = fv.toFloat(); // use as float
+ double d = fv.toDouble(); // use as double
System.out.println("read float: " + d);
break;
case STRING:
- String s = v.get().asString().toString();
+ String s = v.asStringValue().asString();
System.out.println("read string: " + s);
break;
case BINARY:
- // Message buffer is an efficient byte buffer
- MessageBuffer mb = v.get().asBinary().toMessageBuffer();
- System.out.println("read binary: " + mb.toHexString(0, mb.size()));
+ byte[] mb = v.asBinaryValue().asByteArray();
+ System.out.println("read binary: size=" + mb.length);
break;
case ARRAY:
- ArrayValue arr = v.get().asArrayValue();
- for(ValueRef a : arr) {
- System.out.println("read array element: " + a);
+ ArrayValue a = v.asArrayValue();
+ for (Value e : a) {
+ System.out.println("read array element: " + e);
}
break;
- case EXTENDED:
- ExtendedValue ev = v.get().asExtended();
- int extType = ev.getExtType();
- byte[] extValue = ev.toByteArray();
+ case EXTENSION:
+ ExtensionValue ev = v.asExtensionValue();
+ byte extType = ev.getType();
+ byte[] extValue = ev.getData();
break;
}
}
-
}
/**
* Example of using custom MessagePack configuration
+ *
* @throws IOException
*/
- public static void configuration() throws IOException {
-
- // Build a conifiguration
- MessagePack.Config config = new MessagePack.ConfigBuilder()
- .onMalFormedInput(CodingErrorAction.REPLACE) // Drop malformed and unmappable UTF-8 characters
- .onUnmappableCharacter(CodingErrorAction.REPLACE)
- .packerBufferSize(8192 * 2)
- .build();
- // Create a that uses this configuration
- MessagePack msgpack = new MessagePack(config);
-
- // Pack data
+ public static void configuration()
+ throws IOException
+ {
ByteArrayOutputStream out = new ByteArrayOutputStream();
- MessagePacker packer = msgpack.newPacker(out);
+ PackerConfig packerConfig = new PackerConfig();
+ packerConfig.smallStringOptimizationThreshold = 256; // String
+ MessagePacker packer = packerConfig.newPacker(out);
+
packer.packInt(10);
packer.packBoolean(true);
packer.close();
// Unpack data
+ UnpackerConfig unpackerConfig = new UnpackerConfig();
+ unpackerConfig.stringDecoderBufferSize = 16 * 1024; // If your data contains many large strings (the default is 8k)
+
byte[] packedData = out.toByteArray();
- MessageUnpacker unpacker = msgpack.newUnpacker(packedData);
+ MessageUnpacker unpacker = unpackerConfig.newUnpacker(packedData);
int i = unpacker.unpackInt(); // 10
boolean b = unpacker.unpackBoolean(); // true
unpacker.close();
}
-
-
-
-
-
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessageFormatTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessageFormatTest.scala
index c4e381cd0..be9d270cd 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessageFormatTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessageFormatTest.scala
@@ -1,65 +1,84 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core
-import org.scalatest.exceptions.TestFailedException
import org.msgpack.core.MessagePack.Code
-import scala.util.Random
import org.msgpack.value.ValueType
+import org.scalatest.exceptions.TestFailedException
+
+import scala.util.Random
/**
* Created on 2014/05/07.
*/
-class MessageFormatTest extends MessagePackSpec {
-
+class MessageFormatTest
+ extends MessagePackSpec {
"MessageFormat" should {
"cover all byte codes" in {
-
- def checkV(b:Byte, tpe:ValueType) {
+ def checkV(b: Byte, tpe: ValueType) {
try
MessageFormat.valueOf(b).getValueType shouldBe tpe
catch {
- case e:TestFailedException =>
+ case e: TestFailedException =>
error(f"Failure when looking at byte ${b}%02x")
throw e
}
}
- def checkF(b:Byte, f:MessageFormat) {
+ def checkF(b: Byte, f: MessageFormat) {
MessageFormat.valueOf(b) shouldBe f
}
- def check(b:Byte, tpe:ValueType, f:MessageFormat) {
+ def check(b: Byte, tpe: ValueType, f: MessageFormat) {
checkV(b, tpe)
checkF(b, f)
}
- for(i <- 0 until 0x7f)
+ for (i <- 0 until 0x7f) {
check(i.toByte, ValueType.INTEGER, MessageFormat.POSFIXINT)
+ }
- for(i <- 0x80 until 0x8f)
+ for (i <- 0x80 until 0x8f) {
check(i.toByte, ValueType.MAP, MessageFormat.FIXMAP)
+ }
- for(i <- 0x90 until 0x9f)
+ for (i <- 0x90 until 0x9f) {
check(i.toByte, ValueType.ARRAY, MessageFormat.FIXARRAY)
+ }
check(Code.NIL, ValueType.NIL, MessageFormat.NIL)
MessageFormat.valueOf(Code.NEVER_USED) shouldBe MessageFormat.NEVER_USED
- for(i <- Seq(Code.TRUE, Code.FALSE))
+ for (i <- Seq(Code.TRUE, Code.FALSE)) {
check(i, ValueType.BOOLEAN, MessageFormat.BOOLEAN)
+ }
check(Code.BIN8, ValueType.BINARY, MessageFormat.BIN8)
check(Code.BIN16, ValueType.BINARY, MessageFormat.BIN16)
check(Code.BIN32, ValueType.BINARY, MessageFormat.BIN32)
- check(Code.FIXEXT1, ValueType.EXTENDED, MessageFormat.FIXEXT1)
- check(Code.FIXEXT2, ValueType.EXTENDED, MessageFormat.FIXEXT2)
- check(Code.FIXEXT4, ValueType.EXTENDED, MessageFormat.FIXEXT4)
- check(Code.FIXEXT8, ValueType.EXTENDED, MessageFormat.FIXEXT8)
- check(Code.FIXEXT16, ValueType.EXTENDED, MessageFormat.FIXEXT16)
- check(Code.EXT8, ValueType.EXTENDED, MessageFormat.EXT8)
- check(Code.EXT16, ValueType.EXTENDED, MessageFormat.EXT16)
- check(Code.EXT32, ValueType.EXTENDED, MessageFormat.EXT32)
+ check(Code.FIXEXT1, ValueType.EXTENSION, MessageFormat.FIXEXT1)
+ check(Code.FIXEXT2, ValueType.EXTENSION, MessageFormat.FIXEXT2)
+ check(Code.FIXEXT4, ValueType.EXTENSION, MessageFormat.FIXEXT4)
+ check(Code.FIXEXT8, ValueType.EXTENSION, MessageFormat.FIXEXT8)
+ check(Code.FIXEXT16, ValueType.EXTENSION, MessageFormat.FIXEXT16)
+ check(Code.EXT8, ValueType.EXTENSION, MessageFormat.EXT8)
+ check(Code.EXT16, ValueType.EXTENSION, MessageFormat.EXT16)
+ check(Code.EXT32, ValueType.EXTENSION, MessageFormat.EXT32)
check(Code.INT8, ValueType.INTEGER, MessageFormat.INT8)
@@ -82,13 +101,12 @@ class MessageFormatTest extends MessagePackSpec {
check(Code.ARRAY16, ValueType.ARRAY, MessageFormat.ARRAY16)
check(Code.ARRAY32, ValueType.ARRAY, MessageFormat.ARRAY32)
- for(i <- 0xe0 to 0xff)
+ for (i <- 0xe0 to 0xff) {
check(i.toByte, ValueType.INTEGER, MessageFormat.NEGFIXINT)
-
+ }
}
"improve the valueOf performance" in {
-
val N = 1000000
val idx = (0 until N).map(x => Random.nextInt(256).toByte).toArray[Byte]
@@ -98,7 +116,7 @@ class MessageFormatTest extends MessagePackSpec {
time("lookup", repeat = 10) {
block("switch") {
var i = 0
- while(i < N) {
+ while (i < N) {
MessageFormat.toMessageFormat(idx(i))
i += 1
}
@@ -106,18 +124,12 @@ class MessageFormatTest extends MessagePackSpec {
block("table") {
var i = 0
- while(i < N) {
+ while (i < N) {
MessageFormat.valueOf(idx(i))
i += 1
}
}
-
}
-
}
-
}
-
-
-
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackSpec.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackSpec.scala
index 8eec662c4..b8be3bed0 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackSpec.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackSpec.scala
@@ -15,12 +15,14 @@
//
package org.msgpack.core
+import java.io.ByteArrayOutputStream
+
import org.scalatest._
+import org.scalatest.prop.PropertyChecks
import xerial.core.log.{LogLevel, Logger}
import xerial.core.util.{TimeReport, Timer}
+
import scala.language.implicitConversions
-import org.scalatest.prop.PropertyChecks
-import java.io.ByteArrayOutputStream
trait MessagePackSpec
extends WordSpec
@@ -32,43 +34,36 @@ trait MessagePackSpec
with Benchmark
with Logger {
- implicit def toTag(s:String) : Tag = Tag(s)
+ implicit def toTag(s: String): Tag = Tag(s)
- def toHex(arr:Array[Byte]) = arr.map(x => f"$x%02x").mkString(" ")
+ def toHex(arr: Array[Byte]) = arr.map(x => f"$x%02x").mkString(" ")
-
- def createMessagePackData(f: MessagePacker => Unit) : Array[Byte] = {
- val b = new ByteArrayOutputStream()
+ def createMessagePackData(f: MessagePacker => Unit): Array[Byte] = {
+ val b = new
+ ByteArrayOutputStream()
val packer = MessagePack.newDefaultPacker(b)
f(packer)
packer.close()
b.toByteArray
}
-
-
-
-
}
-
-trait Benchmark extends Timer {
+trait Benchmark
+ extends Timer {
val numWarmUpRuns = 10
-
override protected def time[A](blockName: String, logLevel: LogLevel, repeat: Int)(f: => A): TimeReport = {
- super.time(blockName, logLevel=LogLevel.INFO, repeat)(f)
+ super.time(blockName, logLevel = LogLevel.INFO, repeat)(f)
}
override protected def block[A](name: String, repeat: Int)(f: => A): TimeReport = {
var i = 0
- while(i < numWarmUpRuns) {
+ while (i < numWarmUpRuns) {
f
i += 1
}
super.block(name, repeat)(f)
-
}
-
}
\ No newline at end of file
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackTest.scala
index 116160dd1..112c3e5a7 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackTest.scala
@@ -15,26 +15,27 @@
//
package org.msgpack.core
-import org.msgpack.value.Value
-import org.msgpack.value.holder.ValueHolder
-
-import scala.util.Random
-import MessagePack.Code
import java.io.ByteArrayOutputStream
import java.math.BigInteger
import java.nio.CharBuffer
-import java.nio.charset.{UnmappableCharacterException, CodingErrorAction}
+import java.nio.charset.{CodingErrorAction, UnmappableCharacterException}
+
+import org.msgpack.core.MessagePack.Code
+import org.msgpack.core.MessagePack.{UnpackerConfig, PackerConfig}
+import org.msgpack.value.{Value, Variable}
+
+import scala.util.Random
/**
* Created on 2014/05/07.
*/
-class MessagePackTest extends MessagePackSpec {
+class MessagePackTest extends MessagePackSpec {
def isValidUTF8(s: String) = {
MessagePack.UTF8.newEncoder().canEncode(s)
}
- def containsUnmappableCharacter(s: String) : Boolean = {
+ def containsUnmappableCharacter(s: String): Boolean = {
try {
MessagePack.UTF8.newEncoder().onUnmappableCharacter(CodingErrorAction.REPORT).encode(CharBuffer.wrap(s))
false
@@ -59,6 +60,36 @@ class MessagePackTest extends MessagePackSpec {
}
}
+ "detect fixarray values" in {
+ val packer = MessagePack.newDefaultBufferPacker()
+ packer.packArrayHeader(0)
+ packer.close
+ val bytes = packer.toByteArray
+ MessagePack.newDefaultUnpacker(bytes).unpackArrayHeader() shouldBe 0
+ try {
+ MessagePack.newDefaultUnpacker(bytes).unpackMapHeader()
+ fail("Shouldn't reach here")
+ }
+ catch {
+ case e: MessageTypeException => // OK
+ }
+ }
+
+ "detect fixmap values" in {
+ val packer = MessagePack.newDefaultBufferPacker()
+ packer.packMapHeader(0)
+ packer.close
+ val bytes = packer.toByteArray
+ MessagePack.newDefaultUnpacker(bytes).unpackMapHeader() shouldBe 0
+ try {
+ MessagePack.newDefaultUnpacker(bytes).unpackArrayHeader()
+ fail("Shouldn't reach here")
+ }
+ catch {
+ case e: MessageTypeException => // OK
+ }
+ }
+
"detect fixint quickly" in {
val N = 100000
@@ -117,39 +148,52 @@ class MessagePackTest extends MessagePackSpec {
}
- def check[A](v: A, pack: MessagePacker => Unit, unpack: MessageUnpacker => A, msgpack:MessagePack = MessagePack.DEFAULT): Unit = {
+ def check[A](
+ v: A,
+ pack: MessagePacker => Unit,
+ unpack: MessageUnpacker => A,
+ packerConfig: PackerConfig = new PackerConfig(),
+ unpackerConfig: UnpackerConfig = new UnpackerConfig()
+ ): Unit = {
var b: Array[Byte] = null
try {
val bs = new ByteArrayOutputStream()
- val packer = msgpack.newPacker(bs)
+ val packer = packerConfig.newPacker(bs)
pack(packer)
packer.close()
b = bs.toByteArray
- val unpacker = msgpack.newUnpacker(b)
+ val unpacker = unpackerConfig.newUnpacker(b)
val ret = unpack(unpacker)
ret shouldBe v
}
catch {
case e: Exception =>
warn(e.getMessage)
- if (b != null)
+ if (b != null) {
warn(s"packed data (size:${b.length}): ${toHex(b)}")
+ }
throw e
}
}
- def checkException[A](v: A, pack: MessagePacker => Unit, unpack: MessageUnpacker => A, msgpack:MessagePack=MessagePack.DEFAULT) : Unit = {
+ def checkException[A](
+ v: A,
+ pack: MessagePacker => Unit,
+ unpack: MessageUnpacker => A,
+ packerConfig: PackerConfig = new PackerConfig(),
+ unpaackerConfig: UnpackerConfig = new UnpackerConfig()
+ ): Unit = {
var b: Array[Byte] = null
val bs = new ByteArrayOutputStream()
- val packer = msgpack.newPacker(bs)
+ val packer = packerConfig.newPacker(bs)
pack(packer)
packer.close()
b = bs.toByteArray
- val unpacker = msgpack.newUnpacker(b)
+ val unpacker = unpaackerConfig.newUnpacker(b)
val ret = unpack(unpacker)
fail("cannot not reach here")
@@ -160,30 +204,29 @@ class MessagePackTest extends MessagePackSpec {
checkException[A](v, pack, unpack)
}
catch {
- case e:MessageIntegerOverflowException => // OK
+ case e: MessageIntegerOverflowException => // OK
}
}
-
-
-
- "pack/unpack primitive values" taggedAs("prim") in {
- forAll { (v: Boolean) => check(v, _.packBoolean(v), _.unpackBoolean)}
- forAll { (v: Byte) => check(v, _.packByte(v), _.unpackByte)}
- forAll { (v: Short) => check(v, _.packShort(v), _.unpackShort)}
- forAll { (v: Int) => check(v, _.packInt(v), _.unpackInt)}
- forAll { (v: Float) => check(v, _.packFloat(v), _.unpackFloat)}
- forAll { (v: Long) => check(v, _.packLong(v), _.unpackLong)}
- forAll { (v: Double) => check(v, _.packDouble(v), _.unpackDouble)}
- check(null, _.packNil, _.unpackNil())
+ "pack/unpack primitive values" taggedAs ("prim") in {
+ forAll { (v: Boolean) => check(v, _.packBoolean(v), _.unpackBoolean) }
+ forAll { (v: Byte) => check(v, _.packByte(v), _.unpackByte) }
+ forAll { (v: Short) => check(v, _.packShort(v), _.unpackShort) }
+ forAll { (v: Int) => check(v, _.packInt(v), _.unpackInt) }
+ forAll { (v: Float) => check(v, _.packFloat(v), _.unpackFloat) }
+ forAll { (v: Long) => check(v, _.packLong(v), _.unpackLong) }
+ forAll { (v: Double) => check(v, _.packDouble(v), _.unpackDouble) }
+ check(null, _.packNil, { unpacker => unpacker.unpackNil(); null })
}
- "pack/unpack integer values" taggedAs("int") in {
- val sampleData = Seq[Long](Int.MinValue.toLong - 10, -65535, -8191, -1024, -255, -127, -63, -31, -15, -7, -3, -1, 0, 2, 4, 8, 16, 32, 64, 128, 256, 1024, 8192, 65536, Int.MaxValue.toLong + 10)
- for(v <- sampleData) {
+ "pack/unpack integer values" taggedAs ("int") in {
+ val sampleData = Seq[Long](Int.MinValue.toLong -
+ 10, -65535, -8191, -1024, -255, -127, -63, -31, -15, -7, -3, -1, 0, 2, 4, 8, 16, 32, 64, 128, 256, 1024, 8192, 65536,
+ Int.MaxValue.toLong + 10)
+ for (v <- sampleData) {
check(v, _.packLong(v), _.unpackLong)
- if(v.isValidInt) {
+ if (v.isValidInt) {
val vi = v.toInt
check(vi, _.packInt(vi), _.unpackInt)
}
@@ -191,7 +234,7 @@ class MessagePackTest extends MessagePackSpec {
checkOverflow(v, _.packLong(v), _.unpackInt)
}
- if(v.isValidShort) {
+ if (v.isValidShort) {
val vi = v.toShort
check(vi, _.packShort(vi), _.unpackShort)
}
@@ -199,7 +242,7 @@ class MessagePackTest extends MessagePackSpec {
checkOverflow(v, _.packLong(v), _.unpackShort)
}
- if(v.isValidByte) {
+ if (v.isValidByte) {
val vi = v.toByte
check(vi, _.packByte(vi), _.unpackByte)
}
@@ -211,23 +254,23 @@ class MessagePackTest extends MessagePackSpec {
}
- "pack/unpack BigInteger" taggedAs("bi") in {
+ "pack/unpack BigInteger" taggedAs ("bi") in {
forAll { (a: Long) =>
val v = BigInteger.valueOf(a)
check(v, _.packBigInteger(v), _.unpackBigInteger)
}
- for(bi <- Seq(BigInteger.valueOf(Long.MaxValue).add(BigInteger.valueOf(1)))) {
+ for (bi <- Seq(BigInteger.valueOf(Long.MaxValue).add(BigInteger.valueOf(1)))) {
check(bi, _.packBigInteger(bi), _.unpackBigInteger())
}
- for(bi <- Seq(BigInteger.valueOf(Long.MaxValue).shiftLeft(10))) {
+ for (bi <- Seq(BigInteger.valueOf(Long.MaxValue).shiftLeft(10))) {
try {
checkException(bi, _.packBigInteger(bi), _.unpackBigInteger())
fail("cannot reach here")
}
catch {
- case e:IllegalArgumentException => // OK
+ case e: IllegalArgumentException => // OK
}
}
@@ -245,14 +288,14 @@ class MessagePackTest extends MessagePackSpec {
"pack/unpack large strings" taggedAs ("large-string") in {
// Large string
val strLen = Seq(1000, 2000, 10000, 50000, 100000, 500000)
- for(l <- strLen) {
- val v : String = Iterator.continually(Random.nextString(l * 10)).find(isValidUTF8).get
+ for (l <- strLen) {
+ val v: String = Iterator.continually(Random.nextString(l * 10)).find(isValidUTF8).get
check(v, _.packString(v), _.unpackString)
}
}
- "report errors when packing/unpacking malformed strings" taggedAs("malformed") in {
+ "report errors when packing/unpacking malformed strings" taggedAs ("malformed") in {
// TODO produce malformed utf-8 strings in Java8"
pending
// Create 100 malformed UTF8 Strings
@@ -287,45 +330,37 @@ class MessagePackTest extends MessagePackSpec {
}
}
- "report errors when packing/unpacking strings that contain unmappable characters" taggedAs("unmap") in {
+ "report errors when packing/unpacking strings that contain unmappable characters" taggedAs ("unmap") in {
val unmappable = Array[Byte](0xfc.toByte, 0x0a.toByte)
//val unmappableChar = Array[Char](new Character(0xfc0a).toChar)
// Report error on unmappable character
- val config = new MessagePack.ConfigBuilder().onMalFormedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT).build()
- val msgpack = new MessagePack(config)
+ val unpackerConfig = new UnpackerConfig()
+ unpackerConfig.actionOnMalformedString = CodingErrorAction.REPORT
+ unpackerConfig.actionOnUnmappableString = CodingErrorAction.REPORT
- for(bytes <- Seq(unmappable)) {
+ for (bytes <- Seq(unmappable)) {
When("unpacking")
try {
- checkException(bytes,
- { packer =>
+ checkException(bytes, { packer =>
packer.packRawStringHeader(bytes.length)
packer.writePayload(bytes)
},
_.unpackString(),
- msgpack)
+ new PackerConfig(),
+ unpackerConfig)
}
catch {
- case e:MessageStringCodingException => // OK
+ case e: MessageStringCodingException => // OK
}
-
-// When("packing")
-// try {
-// val s = new String(unmappableChar)
-// checkException(s, _.packString(s), _.unpackString())
-// }
-// catch {
-// case e:MessageStringCodingException => // OK
-// }
- }
+ }
}
"pack/unpack binary" taggedAs ("binary") in {
forAll { (v: Array[Byte]) =>
- check(v, { packer => packer.packBinaryHeader(v.length); packer.writePayload(v)}, { unpacker =>
+ check(v, { packer => packer.packBinaryHeader(v.length); packer.writePayload(v) }, { unpacker =>
val len = unpacker.unpackBinaryHeader()
val out = new Array[Byte](len)
unpacker.readPayload(out, 0, len)
@@ -335,10 +370,10 @@ class MessagePackTest extends MessagePackSpec {
}
val len = Seq(1000, 2000, 10000, 50000, 100000, 500000)
- for(l <- len) {
+ for (l <- len) {
val v = new Array[Byte](l)
Random.nextBytes(v)
- check(v, { packer => packer.packBinaryHeader(v.length); packer.writePayload(v)}, { unpacker =>
+ check(v, { packer => packer.packBinaryHeader(v.length); packer.writePayload(v) }, { unpacker =>
val len = unpacker.unpackBinaryHeader()
val out = new Array[Byte](len)
unpacker.readPayload(out, 0, len)
@@ -348,7 +383,7 @@ class MessagePackTest extends MessagePackSpec {
}
}
- val testHeaderLength = Seq(1, 2, 4, 8, 16, 1000, 2000, 10000, 50000, 100000, 500000)
+ val testHeaderLength = Seq(1, 2, 4, 8, 16, 17, 32, 64, 255, 256, 1000, 2000, 10000, 50000, 100000, 500000)
"pack/unpack arrays" taggedAs ("array") in {
@@ -359,14 +394,15 @@ class MessagePackTest extends MessagePackSpec {
}, { unpacker =>
val len = unpacker.unpackArrayHeader()
val out = new Array[Int](len)
- for (i <- 0 until v.length)
+ for (i <- 0 until v.length) {
out(i) = unpacker.unpackInt
+ }
out
}
)
}
- for(l <- testHeaderLength) {
+ for (l <- testHeaderLength) {
check(l, _.packArrayHeader(l), _.unpackArrayHeader())
}
@@ -393,14 +429,15 @@ class MessagePackTest extends MessagePackSpec {
}, { unpacker =>
val len = unpacker.unpackMapHeader()
val b = Seq.newBuilder[(Int, String)]
- for (i <- 0 until len)
+ for (i <- 0 until len) {
b += ((unpacker.unpackInt, unpacker.unpackString))
+ }
b.result
}
)
}
- for(l <- testHeaderLength) {
+ for (l <- testHeaderLength) {
check(l, _.packMapHeader(l), _.unpackMapHeader())
}
@@ -414,19 +451,18 @@ class MessagePackTest extends MessagePackSpec {
}
- "pack/unpack extended types" taggedAs("ext") in {
- forAll { (dataLen: Int, tpe: Int) =>
+ "pack/unpack extension types" taggedAs ("ext") in {
+ forAll { (dataLen: Int, tpe: Byte) =>
val l = Math.abs(dataLen)
- val t = Math.abs(tpe) % 128
whenever(l >= 0) {
- val ext = new ExtendedTypeHeader(l, t)
- check(ext, _.packExtendedTypeHeader(ext.getType, ext.getLength), _.unpackExtendedTypeHeader())
+ val ext = new ExtensionTypeHeader(ExtensionTypeHeader.checkedCastToByte(tpe), l)
+ check(ext, _.packExtensionTypeHeader(ext.getType, ext.getLength), _.unpackExtensionTypeHeader())
}
}
- for(l <- testHeaderLength) {
- val ext = new ExtendedTypeHeader(l, Random.nextInt(128))
- check(ext, _.packExtendedTypeHeader(ext.getType, ext.getLength), _.unpackExtendedTypeHeader())
+ for (l <- testHeaderLength) {
+ val ext = new ExtensionTypeHeader(ExtensionTypeHeader.checkedCastToByte(Random.nextInt(128)), l)
+ check(ext, _.packExtensionTypeHeader(ext.getType, ext.getLength), _.unpackExtensionTypeHeader())
}
}
@@ -444,23 +480,22 @@ class MessagePackTest extends MessagePackSpec {
}
}
}, { unpacker =>
- val holder = new ValueHolder()
- unpacker.unpackValue(holder)
- val v = holder.get()
-
- v.asArrayValue().toValueArray.map { m =>
+ val v = new Variable()
+ unpacker.unpackValue(v)
+ import scala.collection.JavaConversions._
+ v.asArrayValue().map { m =>
val mv = m.asMapValue()
- val kvs = mv.toKeyValueSeq
+ val kvs = mv.getKeyValueArray
kvs.grouped(2).map({ kvp: Array[Value] =>
val k = kvp(0)
val v = kvp(1)
- (k.asString().toString, v.asString().toString)
+ (k.asStringValue().asString, v.asStringValue().asString)
}).toMap
}.toList
})
}
}
-}
\ No newline at end of file
+}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackerTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackerTest.scala
index 2b3499f1e..2fbe461d1 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessagePackerTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessagePackerTest.scala
@@ -15,9 +15,11 @@
//
package org.msgpack.core
-import java.io.ByteArrayOutputStream
+import java.io.{ByteArrayOutputStream, File, FileInputStream, FileOutputStream}
-import org.msgpack.core.buffer.{OutputStreamBufferOutput, ArrayBufferInput}
+import org.msgpack.core.MessagePack.{UnpackerConfig, PackerConfig}
+import org.msgpack.core.buffer.{ChannelBufferOutput, OutputStreamBufferOutput}
+import org.msgpack.value.ValueFactory
import xerial.core.io.IOUtil
import scala.util.Random
@@ -27,12 +29,10 @@ import scala.util.Random
*/
class MessagePackerTest extends MessagePackSpec {
- val msgpack = MessagePack.DEFAULT
-
- def verifyIntSeq(answer:Array[Int], packed:Array[Byte]) {
- val unpacker = msgpack.newUnpacker(packed)
+ def verifyIntSeq(answer: Array[Int], packed: Array[Byte]) {
+ val unpacker = MessagePack.newDefaultUnpacker(packed)
val b = Array.newBuilder[Int]
- while(unpacker.hasNext) {
+ while (unpacker.hasNext) {
b += unpacker.unpackInt()
}
val result = b.result
@@ -40,43 +40,72 @@ class MessagePackerTest extends MessagePackSpec {
result shouldBe answer
}
+ def createTempFile = {
+ val f = File.createTempFile("msgpackTest", "msgpack")
+ f.deleteOnExit
+ f
+ }
+
+ def createTempFileWithOutputStream = {
+ val f = createTempFile
+ val out = new
+ FileOutputStream(f)
+ (f, out)
+ }
+
+ def createTempFileWithChannel = {
+ val (f, out) = createTempFileWithOutputStream
+ val ch = out.getChannel
+ (f, ch)
+ }
+
"MessagePacker" should {
"reset the internal states" in {
val intSeq = (0 until 100).map(i => Random.nextInt).toArray
- val b = new ByteArrayOutputStream
- val packer = msgpack.newPacker(b)
+ val b = new
+ ByteArrayOutputStream
+ val packer = MessagePack.newDefaultPacker(b)
intSeq foreach packer.packInt
packer.close
verifyIntSeq(intSeq, b.toByteArray)
val intSeq2 = intSeq.reverse
- val b2 = new ByteArrayOutputStream
- packer.reset(new OutputStreamBufferOutput(b2))
+ val b2 = new
+ ByteArrayOutputStream
+ packer
+ .reset(new
+ OutputStreamBufferOutput(b2))
intSeq2 foreach packer.packInt
packer.close
verifyIntSeq(intSeq2, b2.toByteArray)
val intSeq3 = intSeq2.sorted
- val b3 = new ByteArrayOutputStream
- packer.reset(new OutputStreamBufferOutput(b3))
+ val b3 = new
+ ByteArrayOutputStream
+ packer
+ .reset(new
+ OutputStreamBufferOutput(b3))
intSeq3 foreach packer.packInt
packer.close
verifyIntSeq(intSeq3, b3.toByteArray)
}
- "improve the performance via reset method" taggedAs("reset") in {
-
+ "improve the performance via reset method" taggedAs ("reset") in {
val N = 1000
val t = time("packer", repeat = 10) {
block("no-buffer-reset") {
- val out = new ByteArrayOutputStream
- IOUtil.withResource(msgpack.newPacker(out)) { packer =>
+ val out = new
+ ByteArrayOutputStream
+ IOUtil.withResource(MessagePack.newDefaultPacker(out)) { packer =>
for (i <- 0 until N) {
- val outputStream = new ByteArrayOutputStream()
- packer.reset(new OutputStreamBufferOutput(outputStream))
+ val outputStream = new
+ ByteArrayOutputStream()
+ packer
+ .reset(new
+ OutputStreamBufferOutput(outputStream))
packer.packInt(0)
packer.flush()
}
@@ -84,11 +113,15 @@ class MessagePackerTest extends MessagePackSpec {
}
block("buffer-reset") {
- val out = new ByteArrayOutputStream
- IOUtil.withResource(msgpack.newPacker(out)) { packer =>
- val bufferOut = new OutputStreamBufferOutput(new ByteArrayOutputStream())
+ val out = new
+ ByteArrayOutputStream
+ IOUtil.withResource(MessagePack.newDefaultPacker(out)) { packer =>
+ val bufferOut = new
+ OutputStreamBufferOutput(new
+ ByteArrayOutputStream())
for (i <- 0 until N) {
- val outputStream = new ByteArrayOutputStream()
+ val outputStream = new
+ ByteArrayOutputStream()
bufferOut.reset(outputStream)
packer.reset(bufferOut)
packer.packInt(0)
@@ -99,8 +132,154 @@ class MessagePackerTest extends MessagePackSpec {
}
t("buffer-reset").averageWithoutMinMax should be <= t("no-buffer-reset").averageWithoutMinMax
+ }
+
+ "pack larger string array than byte buf" taggedAs ("larger-string-array-than-byte-buf") in {
+ // Based on https://github.com/msgpack/msgpack-java/issues/154
+
+ def test(bufferSize: Int, stringSize: Int): Boolean = {
+ val str = "a" * stringSize
+ val rawString = ValueFactory.newString(str.getBytes("UTF-8"))
+ val array = ValueFactory.newArray(rawString)
+ val out = new ByteArrayOutputStream(bufferSize)
+ val packer = MessagePack.newDefaultPacker(out)
+ packer.packValue(array)
+ packer.close()
+ out.toByteArray
+ true
+ }
+ val testCases = Seq(
+ 32 -> 30,
+ 33 -> 31,
+ 32 -> 31,
+ 34 -> 32
+ )
+ testCases.foreach {
+ case (bufferSize, stringSize) => test(bufferSize, stringSize)
+ }
}
+ "reset OutputStreamBufferOutput" in {
+ val (f0, out0) = createTempFileWithOutputStream
+ val packer = MessagePack.newDefaultPacker(out0)
+ packer.packInt(99)
+ packer.close
+
+ val up0 = MessagePack
+ .newDefaultUnpacker(new
+ FileInputStream(f0))
+ up0.unpackInt shouldBe 99
+ up0.hasNext shouldBe false
+ up0.close
+
+ val (f1, out1) = createTempFileWithOutputStream
+ packer
+ .reset(new
+ OutputStreamBufferOutput(out1))
+ packer.packInt(99)
+ packer.flush
+ packer
+ .reset(new
+ OutputStreamBufferOutput(out1))
+ packer.packString("hello")
+ packer.close
+
+ val up1 = MessagePack
+ .newDefaultUnpacker(new
+ FileInputStream(f1))
+ up1.unpackInt shouldBe 99
+ up1.unpackString shouldBe "hello"
+ up1.hasNext shouldBe false
+ up1.close
+ }
+
+ "reset ChannelBufferOutput" in {
+ val (f0, out0) = createTempFileWithChannel
+ val packer = MessagePack.newDefaultPacker(out0)
+ packer.packInt(99)
+ packer.close
+
+ val up0 = MessagePack
+ .newDefaultUnpacker(new
+ FileInputStream(f0))
+ up0.unpackInt shouldBe 99
+ up0.hasNext shouldBe false
+ up0.close
+
+ val (f1, out1) = createTempFileWithChannel
+ packer
+ .reset(new
+ ChannelBufferOutput(out1))
+ packer.packInt(99)
+ packer.flush
+ packer
+ .reset(new
+ ChannelBufferOutput(out1))
+ packer.packString("hello")
+ packer.close
+
+ val up1 = MessagePack
+ .newDefaultUnpacker(new
+ FileInputStream(f1))
+ up1.unpackInt shouldBe 99
+ up1.unpackString shouldBe "hello"
+ up1.hasNext shouldBe false
+ up1.close
+ }
+
+ "pack a lot of String within expected time" in {
+ val count = 20000
+
+ def measureDuration(outputStream: java.io.OutputStream) = {
+ val packer = MessagePack.newDefaultPacker(outputStream)
+ var i = 0
+ while (i < count) {
+ packer.packString("0123456789ABCDEF")
+ i += 1
+ }
+ packer.close
+ }
+
+ val t = time("packString into OutputStream", repeat = 10) {
+ block("byte-array-output-stream") {
+ measureDuration(new ByteArrayOutputStream())
+ }
+
+ block("file-output-stream") {
+ val (_, fileOutput) = createTempFileWithOutputStream
+ measureDuration(fileOutput)
+ }
+ }
+ t("file-output-stream").averageWithoutMinMax shouldBe < (t("byte-array-output-stream").averageWithoutMinMax * 5)
+ }
+ }
+
+ "compute totalWrittenBytes" in {
+ val out = new
+ ByteArrayOutputStream
+ val packerTotalWrittenBytes = IOUtil.withResource(MessagePack.newDefaultPacker(out)) { packer =>
+ packer.packByte(0) // 1
+ .packBoolean(true) // 1
+ .packShort(12) // 1
+ .packInt(1024) // 3
+ .packLong(Long.MaxValue) // 5
+ .packString("foobar") // 7
+ .flush()
+
+ packer.getTotalWrittenBytes
+ }
+
+ out.toByteArray.length shouldBe packerTotalWrittenBytes
+ }
+
+ "support read-only buffer" taggedAs ("read-only") in {
+ val payload = Array[Byte](1)
+ val out = new
+ ByteArrayOutputStream()
+ val packer = MessagePack.newDefaultPacker(out)
+ .packBinaryHeader(1)
+ .writePayload(payload)
+ .close()
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/MessageUnpackerTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/MessageUnpackerTest.scala
index cb262b1ba..e8ed65be7 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/MessageUnpackerTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/MessageUnpackerTest.scala
@@ -1,21 +1,37 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core
-import java.io.{EOFException, ByteArrayInputStream, ByteArrayOutputStream}
-import scala.util.Random
-import org.msgpack.core.buffer.{MessageBuffer, MessageBufferInput, OutputStreamBufferOutput, ArrayBufferInput}
+import java.io._
+import java.nio.ByteBuffer
+
+import org.msgpack.core.buffer._
import org.msgpack.value.ValueType
import xerial.core.io.IOUtil
+import scala.util.Random
+
/**
* Created on 2014/05/07.
*/
class MessageUnpackerTest extends MessagePackSpec {
- val msgpack = MessagePack.DEFAULT
-
- def testData : Array[Byte] = {
+ def testData: Array[Byte] = {
val out = new ByteArrayOutputStream()
- val packer = msgpack.newPacker(out)
+ val packer = MessagePack.newDefaultPacker(out)
packer
.packArrayHeader(2)
@@ -33,12 +49,11 @@ class MessageUnpackerTest extends MessagePackSpec {
arr
}
- val intSeq = (for(i <- 0 until 100) yield Random.nextInt()).toArray[Int]
-
- def testData2 : Array[Byte] = {
+ val intSeq = (for (i <- 0 until 100) yield Random.nextInt()).toArray[Int]
+ def testData2: Array[Byte] = {
val out = new ByteArrayOutputStream()
- val packer = msgpack.newPacker(out);
+ val packer = MessagePack.newDefaultPacker(out);
packer
.packBoolean(true)
@@ -52,7 +67,7 @@ class MessageUnpackerTest extends MessagePackSpec {
arr
}
- def write(packer:MessagePacker, r:Random) {
+ def write(packer: MessagePacker, r: Random) {
val tpeIndex = Iterator.continually(r.nextInt(MessageFormat.values().length)).find(_ != MessageFormat.NEVER_USED.ordinal()).get
val tpe = MessageFormat.values()(tpeIndex)
tpe.getValueType match {
@@ -85,7 +100,7 @@ class MessageUnpackerTest extends MessagePackSpec {
trace(s"array len: $len")
packer.packArrayHeader(len)
var i = 0
- while(i < len) {
+ while (i < len) {
write(packer, r)
i += 1
}
@@ -94,7 +109,7 @@ class MessageUnpackerTest extends MessagePackSpec {
packer.packMapHeader(len)
trace(s"map len: ${len}")
var i = 0
- while(i < len * 2) {
+ while (i < len * 2) {
write(packer, r)
i += 1
}
@@ -105,10 +120,10 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- def testData3(N:Int) : Array[Byte] = {
+ def testData3(N: Int): Array[Byte] = {
val out = new ByteArrayOutputStream()
- val packer = msgpack.newPacker(out)
+ val packer = MessagePack.newDefaultPacker(out)
val r = new Random(0)
@@ -122,7 +137,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
- def readValue(unpacker:MessageUnpacker) {
+ def readValue(unpacker: MessageUnpacker) {
val f = unpacker.getNextFormat()
f.getValueType match {
case ValueType.ARRAY =>
@@ -143,16 +158,29 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
+ def createTempFile = {
+ val f = File.createTempFile("msgpackTest", "msgpack")
+ f.deleteOnExit
+ val p = MessagePack.newDefaultPacker(new FileOutputStream(f))
+ p.packInt(99)
+ p.close
+ f
+ }
+
+ def checkFile(u: MessageUnpacker) = {
+ u.unpackInt shouldBe 99
+ u.hasNext shouldBe false
+ }
"MessageUnpacker" should {
- "parse message packed data" taggedAs("unpack") in {
+ "parse message packed data" taggedAs ("unpack") in {
val arr = testData
- val unpacker = msgpack.newUnpacker(arr)
+ val unpacker = MessagePack.newDefaultUnpacker(arr)
var count = 0
- while(unpacker.hasNext) {
+ while (unpacker.hasNext) {
count += 1
readValue(unpacker)
}
@@ -162,9 +190,9 @@ class MessageUnpackerTest extends MessagePackSpec {
"skip reading values" in {
- val unpacker = msgpack.newUnpacker(testData)
+ val unpacker = MessagePack.newDefaultUnpacker(testData)
var skipCount = 0
- while(unpacker.hasNext) {
+ while (unpacker.hasNext) {
unpacker.skipValue()
skipCount += 1
}
@@ -173,15 +201,15 @@ class MessageUnpackerTest extends MessagePackSpec {
unpacker.getTotalReadBytes shouldBe testData.length
}
- "compare skip performance" taggedAs("skip") in {
+ "compare skip performance" taggedAs ("skip") in {
val N = 10000
val data = testData3(N)
time("skip performance", repeat = 100) {
block("switch") {
- val unpacker = msgpack.newUnpacker(data)
+ val unpacker = MessagePack.newDefaultUnpacker(data)
var skipCount = 0
- while(unpacker.hasNext) {
+ while (unpacker.hasNext) {
unpacker.skipValue()
skipCount += 1
}
@@ -197,8 +225,8 @@ class MessageUnpackerTest extends MessagePackSpec {
val ib = Seq.newBuilder[Int]
- val unpacker = msgpack.newUnpacker(testData2)
- while(unpacker.hasNext) {
+ val unpacker = MessagePack.newDefaultUnpacker(testData2)
+ while (unpacker.hasNext) {
val f = unpacker.getNextFormat
f.getValueType match {
case ValueType.INTEGER =>
@@ -218,7 +246,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
- class SplitMessageBufferInput(array:Array[Array[Byte]]) extends MessageBufferInput {
+ class SplitMessageBufferInput(array: Array[Array[Byte]]) extends MessageBufferInput {
var cursor = 0
override def next(): MessageBuffer = {
if (cursor < array.length) {
@@ -226,19 +254,20 @@ class MessageUnpackerTest extends MessagePackSpec {
cursor += 1
MessageBuffer.wrap(a)
}
- else
+ else {
null
+ }
}
override def close(): Unit = {}
}
- "read data at the buffer boundary" taggedAs("boundary") in {
+ "read data at the buffer boundary" taggedAs ("boundary") in {
trait SplitTest {
- val data : Array[Byte]
+ val data: Array[Byte]
def run {
- val unpacker = msgpack.newUnpacker(data)
+ val unpacker = MessagePack.newDefaultUnpacker(data)
val numElems = {
var c = 0
while (unpacker.hasNext) {
@@ -252,7 +281,7 @@ class MessageUnpackerTest extends MessagePackSpec {
debug(s"split at $splitPoint")
val (h, t) = data.splitAt(splitPoint)
val bin = new SplitMessageBufferInput(Array(h, t))
- val unpacker = new MessageUnpacker(bin)
+ val unpacker = MessagePack.newDefaultUnpacker(bin)
var count = 0
while (unpacker.hasNext) {
count += 1
@@ -265,12 +294,12 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- new SplitTest { val data = testData }.run
- new SplitTest { val data = testData3(30) }.run
+ new SplitTest {val data = testData}.run
+ new SplitTest {val data = testData3(30)}.run
}
- "be faster then msgpack-v6 skip" taggedAs("cmp-skip") in {
+ "be faster then msgpack-v6 skip" taggedAs ("cmp-skip") in {
val data = testData3(10000)
val N = 100
@@ -295,7 +324,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
block("v7") {
- val unpacker = msgpack.newUnpacker(data)
+ val unpacker = MessagePack.newDefaultUnpacker(data)
var count = 0
try {
while (unpacker.hasNext) {
@@ -311,22 +340,22 @@ class MessageUnpackerTest extends MessagePackSpec {
t("v7").averageWithoutMinMax should be <= t("v6").averageWithoutMinMax
}
- import org.msgpack.`type`.{ValueType=>ValueTypeV6}
+ import org.msgpack.`type`.{ValueType => ValueTypeV6}
- "be faster than msgpack-v6 read value" taggedAs("cmp-unpack") in {
+ "be faster than msgpack-v6 read value" taggedAs ("cmp-unpack") in {
- def readValueV6(unpacker:org.msgpack.unpacker.MessagePackUnpacker) {
+ def readValueV6(unpacker: org.msgpack.unpacker.MessagePackUnpacker) {
val vt = unpacker.getNextType()
vt match {
case ValueTypeV6.ARRAY =>
val len = unpacker.readArrayBegin()
var i = 0
- while(i < len) { readValueV6(unpacker); i += 1 }
+ while (i < len) {readValueV6(unpacker); i += 1}
unpacker.readArrayEnd()
case ValueTypeV6.MAP =>
val len = unpacker.readMapBegin()
var i = 0
- while(i < len) { readValueV6(unpacker); readValueV6(unpacker); i += 1 }
+ while (i < len) {readValueV6(unpacker); readValueV6(unpacker); i += 1}
unpacker.readMapEnd()
case ValueTypeV6.NIL =>
unpacker.readNil()
@@ -345,18 +374,18 @@ class MessageUnpackerTest extends MessagePackSpec {
val buf = new Array[Byte](8192)
- def readValue(unpacker:MessageUnpacker) {
+ def readValue(unpacker: MessageUnpacker) {
val f = unpacker.getNextFormat
val vt = f.getValueType
vt match {
case ValueType.ARRAY =>
val len = unpacker.unpackArrayHeader()
var i = 0
- while(i < len) { readValue(unpacker); i += 1 }
+ while (i < len) {readValue(unpacker); i += 1}
case ValueType.MAP =>
val len = unpacker.unpackMapHeader()
var i = 0
- while(i < len) { readValue(unpacker); readValue(unpacker); i += 1 }
+ while (i < len) {readValue(unpacker); readValue(unpacker); i += 1}
case ValueType.NIL =>
unpacker.unpackNil()
case ValueType.INTEGER =>
@@ -378,26 +407,26 @@ class MessageUnpackerTest extends MessagePackSpec {
val data = testData3(10000)
val N = 100
- val t = time("unpack performance", repeat=N) {
+ val t = time("unpack performance", repeat = N) {
block("v6") {
val v6 = new org.msgpack.MessagePack()
val unpacker = new org.msgpack.unpacker.MessagePackUnpacker(v6, new ByteArrayInputStream(data))
var count = 0
try {
- while(true) {
+ while (true) {
readValueV6(unpacker)
count += 1
}
}
catch {
- case e:EOFException =>
+ case e: EOFException =>
}
finally
unpacker.close()
}
block("v7") {
- val unpacker = msgpack.newUnpacker(data)
+ val unpacker = MessagePack.newDefaultUnpacker(data)
var count = 0
try {
while (unpacker.hasNext) {
@@ -415,10 +444,10 @@ class MessageUnpackerTest extends MessagePackSpec {
}
- "be faster for reading binary than v6" taggedAs("cmp-binary") in {
+ "be faster for reading binary than v6" taggedAs ("cmp-binary") in {
val bos = new ByteArrayOutputStream()
- val packer = msgpack.newPacker(bos)
+ val packer = MessagePack.newDefaultPacker(bos)
val L = 10000
val R = 100
(0 until R).foreach { i =>
@@ -428,12 +457,12 @@ class MessageUnpackerTest extends MessagePackSpec {
packer.close()
val b = bos.toByteArray
- time("unpackBinary", repeat=100) {
+ time("unpackBinary", repeat = 100) {
block("v6") {
val v6 = new org.msgpack.MessagePack()
val unpacker = new org.msgpack.unpacker.MessagePackUnpacker(v6, new ByteArrayInputStream(b))
var i = 0
- while(i < R) {
+ while (i < R) {
val out = unpacker.readByteArray()
i += 1
}
@@ -441,9 +470,9 @@ class MessageUnpackerTest extends MessagePackSpec {
}
block("v7") {
- val unpacker = msgpack.newUnpacker(b)
+ val unpacker = MessagePack.newDefaultUnpacker(b)
var i = 0
- while(i < R) {
+ while (i < R) {
val len = unpacker.unpackBinaryHeader()
val out = new Array[Byte](len)
unpacker.readPayload(out, 0, len)
@@ -453,9 +482,9 @@ class MessageUnpackerTest extends MessagePackSpec {
}
block("v7-ref") {
- val unpacker = msgpack.newUnpacker(b)
+ val unpacker = MessagePack.newDefaultUnpacker(b)
var i = 0
- while(i < R) {
+ while (i < R) {
val len = unpacker.unpackBinaryHeader()
val out = unpacker.readPayloadAsReference(len)
i += 1
@@ -465,21 +494,21 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- "read payload as a reference" taggedAs("ref") in {
+ "read payload as a reference" taggedAs ("ref") in {
val dataSizes = Seq(0, 1, 5, 8, 16, 32, 128, 256, 1024, 2000, 10000, 100000)
- for(s <- dataSizes) {
+ for (s <- dataSizes) {
When(f"data size is $s%,d")
val data = new Array[Byte](s)
Random.nextBytes(data)
val b = new ByteArrayOutputStream()
- val packer = msgpack.newPacker(b)
+ val packer = MessagePack.newDefaultPacker(b)
packer.packBinaryHeader(s)
packer.writePayload(data)
packer.close()
- val unpacker = msgpack.newUnpacker(b.toByteArray)
+ val unpacker = MessagePack.newDefaultUnpacker(b.toByteArray)
val len = unpacker.unpackBinaryHeader()
len shouldBe s
val ref = unpacker.readPayloadAsReference(len)
@@ -494,14 +523,14 @@ class MessageUnpackerTest extends MessagePackSpec {
}
- "reset the internal states" taggedAs("reset") in {
+ "reset the internal states" taggedAs ("reset") in {
val data = intSeq
val b = createMessagePackData(packer => data foreach packer.packInt)
- val unpacker = msgpack.newUnpacker(b)
+ val unpacker = MessagePack.newDefaultUnpacker(b)
val unpacked = Array.newBuilder[Int]
- while(unpacker.hasNext) {
+ while (unpacker.hasNext) {
unpacked += unpacker.unpackInt()
}
unpacker.close
@@ -512,7 +541,7 @@ class MessageUnpackerTest extends MessagePackSpec {
val bi = new ArrayBufferInput(b2)
unpacker.reset(bi)
val unpacked2 = Array.newBuilder[Int]
- while(unpacker.hasNext) {
+ while (unpacker.hasNext) {
unpacked2 += unpacker.unpackInt()
}
unpacker.close
@@ -522,7 +551,7 @@ class MessageUnpackerTest extends MessagePackSpec {
bi.reset(b2)
unpacker.reset(bi)
val unpacked3 = Array.newBuilder[Int]
- while(unpacker.hasNext) {
+ while (unpacker.hasNext) {
unpacked3 += unpacker.unpackInt()
}
unpacker.close
@@ -530,10 +559,10 @@ class MessageUnpackerTest extends MessagePackSpec {
}
- "improve the performance via reset method" taggedAs("reset-arr") in {
+ "improve the performance via reset method" taggedAs ("reset-arr") in {
val out = new ByteArrayOutputStream
- val packer = msgpack.newPacker(out)
+ val packer = MessagePack.newDefaultPacker(out)
packer.packInt(0)
packer.flush
val arr = out.toByteArray
@@ -542,7 +571,7 @@ class MessageUnpackerTest extends MessagePackSpec {
val N = 1000
val t = time("unpacker", repeat = 10) {
block("no-buffer-reset") {
- IOUtil.withResource(msgpack.newUnpacker(arr)) { unpacker =>
+ IOUtil.withResource(MessagePack.newDefaultUnpacker(arr)) { unpacker =>
for (i <- 0 until N) {
val buf = new ArrayBufferInput(arr)
unpacker.reset(buf)
@@ -553,7 +582,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
block("reuse-array-input") {
- IOUtil.withResource(msgpack.newUnpacker(arr)) { unpacker =>
+ IOUtil.withResource(MessagePack.newDefaultUnpacker(arr)) { unpacker =>
val buf = new ArrayBufferInput(arr)
for (i <- 0 until N) {
buf.reset(arr)
@@ -565,7 +594,7 @@ class MessageUnpackerTest extends MessagePackSpec {
}
block("reuse-message-buffer") {
- IOUtil.withResource(msgpack.newUnpacker(arr)) { unpacker =>
+ IOUtil.withResource(MessagePack.newDefaultUnpacker(arr)) { unpacker =>
val buf = new ArrayBufferInput(arr)
for (i <- 0 until N) {
buf.reset(mb)
@@ -577,9 +606,86 @@ class MessageUnpackerTest extends MessagePackSpec {
}
}
- t("reuse-message-buffer").averageWithoutMinMax should be <= t("no-buffer-reset").averageWithoutMinMax
- // This performance comparition is too close, so we disabled it
+ // This performance comparison is too close, so we disabled it
+ // t("reuse-message-buffer").averageWithoutMinMax should be <= t("no-buffer-reset").averageWithoutMinMax
// t("reuse-array-input").averageWithoutMinMax should be <= t("no-buffer-reset").averageWithoutMinMax
}
+
+ "reset ChannelBufferInput" in {
+ val f0 = createTempFile
+ val u = MessagePack.newDefaultUnpacker(new FileInputStream(f0).getChannel)
+ checkFile(u)
+
+ val f1 = createTempFile
+ val ch = new FileInputStream(f1).getChannel
+ u.reset(new ChannelBufferInput(ch))
+ checkFile(u)
+ u.close
+ }
+
+ "reset InputStreamBufferInput" in {
+ val f0 = createTempFile
+ val u = MessagePack.newDefaultUnpacker(new FileInputStream(f0))
+ checkFile(u)
+
+ val f1 = createTempFile
+ val in = new FileInputStream(f1)
+ u.reset(new InputStreamBufferInput(in))
+ checkFile(u)
+ u.close
+ }
+
+ "unpack large string data" taggedAs ("large-string") in {
+ def createLargeData(stringLength: Int): Array[Byte] = {
+ val out = new ByteArrayOutputStream()
+ val packer = MessagePack.newDefaultPacker(out)
+
+ packer
+ .packArrayHeader(2)
+ .packString("l" * stringLength)
+ .packInt(1)
+
+ packer.close()
+
+ out.toByteArray
+ }
+
+ Seq(8191, 8192, 8193, 16383, 16384, 16385).foreach { n =>
+ val arr = createLargeData(n)
+
+ val unpacker = MessagePack.newDefaultUnpacker(arr)
+
+ unpacker.unpackArrayHeader shouldBe 2
+ unpacker.unpackString.length shouldBe n
+ unpacker.unpackInt shouldBe 1
+
+ unpacker.getTotalReadBytes shouldBe arr.length
+ }
+ }
+
+ "unpack string crossing end of buffer" in {
+ def check(expected: String, strLen: Int) = {
+ val bytes = new Array[Byte](strLen)
+ val out = new ByteArrayOutputStream
+
+ val packer = MessagePack.newDefaultPacker(out)
+ packer.packBinaryHeader(bytes.length)
+ packer.writePayload(bytes)
+ packer.packString(expected)
+ packer.close
+
+ val unpacker = MessagePack.newDefaultUnpacker(new InputStreamBufferInput(new ByteArrayInputStream(out.toByteArray)))
+ val len = unpacker.unpackBinaryHeader
+ unpacker.readPayload(len)
+ val got = unpacker.unpackString
+ unpacker.close
+
+ got shouldBe expected
+ }
+
+ Seq("\u3042", "a\u3042", "\u3042a", "\u3042\u3044\u3046\u3048\u304A\u304B\u304D\u304F\u3051\u3053\u3055\u3057\u3059\u305B\u305D").foreach { s =>
+ Seq(8185, 8186, 8187, 8188, 16377, 16378, 16379, 16380).foreach { n => check(s, n)}
+ }
+ }
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/buffer/ByteStringTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/buffer/ByteStringTest.scala
new file mode 100644
index 000000000..2c080b59a
--- /dev/null
+++ b/msgpack-core/src/test/scala/org/msgpack/core/buffer/ByteStringTest.scala
@@ -0,0 +1,46 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer
+
+import akka.util.ByteString
+import org.msgpack.core.{MessagePack, MessagePackSpec, MessageUnpacker}
+
+class ByteStringTest
+ extends MessagePackSpec {
+
+ val unpackedString = "foo"
+ val byteString = ByteString(createMessagePackData(_.packString(unpackedString)))
+
+ def unpackString(messageBuffer: MessageBuffer) = {
+ val input = new
+ MessageBufferInput {
+
+ private var isRead = false
+
+ override def next(): MessageBuffer =
+ if (isRead) {
+ null
+ }
+ else {
+ isRead = true
+ messageBuffer
+ }
+ override def close(): Unit = {}
+ }
+
+ MessagePack.newDefaultUnpacker(input).unpackString()
+ }
+}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala
index dbae8799c..1638806ee 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferInputTest.scala
@@ -1,22 +1,38 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer
-import org.msgpack.core.MessagePackSpec
import java.io._
-import xerial.core.io.IOUtil
-import scala.util.Random
-import java.util.zip.{GZIPOutputStream, GZIPInputStream}
import java.nio.ByteBuffer
+import java.util.zip.{GZIPInputStream, GZIPOutputStream}
+
+import org.msgpack.core.{MessagePack, MessagePackSpec, MessageUnpacker}
+import xerial.core.io.IOUtil._
-/**
- * Created on 5/30/14.
- */
-class MessageBufferInputTest extends MessagePackSpec {
+import scala.util.Random
+
+class MessageBufferInputTest
+ extends MessagePackSpec {
val targetInputSize = Seq(0, 10, 500, 1000, 2000, 4000, 8000, 10000, 30000, 50000, 100000)
- def testData(size:Int) = {
+ def testData(size: Int) = {
//debug(s"test data size: ${size}")
- val b = new Array[Byte](size)
+ val b = new
+ Array[Byte](size)
Random.nextBytes(b)
b
}
@@ -24,17 +40,19 @@ class MessageBufferInputTest extends MessagePackSpec {
def testDataSet = {
targetInputSize.map(testData)
}
-
- def runTest(factory:Array[Byte] => MessageBufferInput) {
- for(b <- testDataSet) {
+
+ def runTest(factory: Array[Byte] => MessageBufferInput) {
+ for (b <- testDataSet) {
checkInputData(b, factory(b))
}
}
- implicit class InputData(b:Array[Byte]) {
+ implicit class InputData(b: Array[Byte]) {
def compress = {
- val compressed = new ByteArrayOutputStream()
- val out = new GZIPOutputStream(compressed)
+ val compressed = new
+ ByteArrayOutputStream()
+ val out = new
+ GZIPOutputStream(compressed)
out.write(b)
out.close()
compressed.toByteArray
@@ -44,58 +62,139 @@ class MessageBufferInputTest extends MessagePackSpec {
ByteBuffer.wrap(b)
}
- def saveToTmpFile : File = {
- val tmp = File.createTempFile("testbuf", ".dat", new File("target"))
+ def saveToTmpFile: File = {
+ val tmp = File
+ .createTempFile("testbuf",
+ ".dat",
+ new
+ File("target"))
tmp.getParentFile.mkdirs()
tmp.deleteOnExit()
- IOUtil.withResource(new FileOutputStream(tmp)) { out =>
+ withResource(new
+ FileOutputStream(tmp)) { out =>
out.write(b)
}
tmp
}
}
-
-
- def checkInputData(inputData:Array[Byte], in:MessageBufferInput) {
+ def checkInputData(inputData: Array[Byte], in: MessageBufferInput) {
When(s"input data size = ${inputData.length}")
var cursor = 0
- for(m <- Iterator.continually(in.next).takeWhile(_ != null)) {
+ for (m <- Iterator.continually(in.next).takeWhile(_ != null)) {
m.toByteArray() shouldBe inputData.slice(cursor, cursor + m.size())
cursor += m.size()
}
cursor shouldBe inputData.length
-
}
-
"MessageBufferInput" should {
"support byte arrays" in {
- runTest(new ArrayBufferInput(_))
+ runTest(new
+ ArrayBufferInput(_))
}
- "support ByteBuffers" in {
- runTest(b => new ByteBufferInput(b.toByteBuffer))
- }
-
- "support InputStreams" taggedAs("is") in {
- runTest(b =>
- new InputStreamBufferInput(
- new GZIPInputStream(new ByteArrayInputStream(b.compress)))
+ "support InputStreams" taggedAs ("is") in {
+ runTest(b =>
+ new
+ InputStreamBufferInput(
+ new
+ GZIPInputStream(new
+ ByteArrayInputStream(b.compress)))
)
}
- "support file input channel" taggedAs("fc") in {
+ "support file input channel" taggedAs ("fc") in {
runTest { b =>
val tmp = b.saveToTmpFile
try {
- InputStreamBufferInput.newBufferInput(new FileInputStream(tmp))
+ InputStreamBufferInput
+ .newBufferInput(new
+ FileInputStream(tmp))
}
finally {
tmp.delete()
}
}
}
+ }
+
+ def createTempFile = {
+ val f = File.createTempFile("msgpackTest", "msgpack")
+ f.deleteOnExit
+ f
+ }
+
+ def createTempFileWithInputStream = {
+ val f = createTempFile
+ val out = new FileOutputStream(f)
+ MessagePack.newDefaultPacker(out).packInt(42).close
+ val in = new
+ FileInputStream(f)
+ (f, in)
+ }
+
+ def createTempFileWithChannel = {
+ val (f, in) = createTempFileWithInputStream
+ val ch = in.getChannel
+ (f, ch)
+ }
+ def readInt(buf: MessageBufferInput): Int = {
+ val unpacker = MessagePack.newDefaultUnpacker(buf)
+ unpacker.unpackInt
+ }
+
+ "InputStreamBufferInput" should {
+ "reset buffer" in {
+ val (f0, in0) = createTempFileWithInputStream
+ val buf = new
+ InputStreamBufferInput(in0)
+ readInt(buf) shouldBe 42
+
+ val (f1, in1) = createTempFileWithInputStream
+ buf.reset(in1)
+ readInt(buf) shouldBe 42
+ }
+
+ "be non-blocking" taggedAs ("non-blocking") in {
+
+ withResource(new
+ PipedOutputStream()) { pipedOutputStream =>
+ withResource(new
+ PipedInputStream()) { pipedInputStream =>
+ pipedInputStream.connect(pipedOutputStream)
+
+ val packer = MessagePack.newDefaultPacker(pipedOutputStream)
+ .packArrayHeader(2)
+ .packLong(42)
+ .packString("hello world")
+
+ packer.flush
+
+ val unpacker = MessagePack.newDefaultUnpacker(pipedInputStream)
+ unpacker.hasNext() shouldBe true
+ unpacker.unpackArrayHeader() shouldBe 2
+ unpacker.unpackLong() shouldBe 42L
+ unpacker.unpackString() shouldBe "hello world"
+
+ packer.close
+ unpacker.close
+ }
+ }
+ }
+ }
+
+ "ChannelBufferInput" should {
+ "reset buffer" in {
+ val (f0, in0) = createTempFileWithChannel
+ val buf = new
+ ChannelBufferInput(in0)
+ readInt(buf) shouldBe 42
+
+ val (f1, in1) = createTempFileWithChannel
+ buf.reset(in1)
+ readInt(buf) shouldBe 42
+ }
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferOutputTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferOutputTest.scala
new file mode 100644
index 000000000..1869f2aad
--- /dev/null
+++ b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferOutputTest.scala
@@ -0,0 +1,80 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer
+
+import java.io._
+
+import org.msgpack.core.MessagePackSpec
+
+class MessageBufferOutputTest
+ extends MessagePackSpec {
+
+ def createTempFile = {
+ val f = File.createTempFile("msgpackTest", "msgpack")
+ f.deleteOnExit
+ f
+ }
+
+ def createTempFileWithOutputStream = {
+ val f = createTempFile
+ val out = new
+ FileOutputStream(f)
+ (f, out)
+ }
+
+ def createTempFileWithChannel = {
+ val (f, out) = createTempFileWithOutputStream
+ val ch = out.getChannel
+ (f, ch)
+ }
+
+ def writeIntToBuf(buf: MessageBufferOutput) = {
+ val mb0 = buf.next(8)
+ mb0.putInt(0, 42)
+ buf.writeBuffer(4)
+ buf.close
+ }
+
+ "OutputStreamBufferOutput" should {
+ "reset buffer" in {
+ val (f0, out0) = createTempFileWithOutputStream
+ val buf = new
+ OutputStreamBufferOutput(out0)
+ writeIntToBuf(buf)
+ f0.length.toInt should be > 0
+
+ val (f1, out1) = createTempFileWithOutputStream
+ buf.reset(out1)
+ writeIntToBuf(buf)
+ f1.length.toInt should be > 0
+ }
+ }
+
+ "ChannelBufferOutput" should {
+ "reset buffer" in {
+ val (f0, ch0) = createTempFileWithChannel
+ val buf = new
+ ChannelBufferOutput(ch0)
+ writeIntToBuf(buf)
+ f0.length.toInt should be > 0
+
+ val (f1, ch1) = createTempFileWithChannel
+ buf.reset(ch1)
+ writeIntToBuf(buf)
+ f1.length.toInt should be > 0
+ }
+ }
+}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferTest.scala
index 15bab6d6f..75ef00a11 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/buffer/MessageBufferTest.scala
@@ -1,40 +1,71 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.buffer
import java.nio.ByteBuffer
-import scala.util.Random
+
import org.msgpack.core.MessagePackSpec
+import scala.util.Random
+
/**
* Created on 2014/05/01.
*/
-class MessageBufferTest extends MessagePackSpec {
+class MessageBufferTest
+ extends MessagePackSpec {
"MessageBuffer" should {
+ "check buffer type" in {
+ val b = MessageBuffer.allocate(0)
+ info(s"MessageBuffer type: ${b.getClass.getName}")
+ }
+
+ "wrap ByteBuffer considering position and remaining values" taggedAs ("wrap-bb") in {
+ val d = Array[Byte](10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
+ val mb = MessageBuffer.wrap(d, 2, 2)
+ mb.getByte(0) shouldBe 12
+ mb.size() shouldBe 2
+ }
+
"have better performance than ByteBuffer" in {
val N = 1000000
val M = 64 * 1024 * 1024
- val ub = MessageBuffer.newBuffer(M)
- val ud = MessageBuffer.newDirectBuffer(M)
+ val ub = MessageBuffer.allocate(M)
val hb = ByteBuffer.allocate(M)
val db = ByteBuffer.allocateDirect(M)
def bench(f: Int => Unit) {
var i = 0
- while(i < N) {
+ while (i < N) {
f((i * 4) % M)
i += 1
}
}
- val r = new Random(0)
- val rs = new Array[Int](N)
+ val r = new
+ Random(0)
+ val rs = new
+ Array[Int](N)
(0 until N).map(i => rs(i) = r.nextInt(N))
def randomBench(f: Int => Unit) {
var i = 0
- while(i < N) {
+ while (i < N) {
f((rs(i) * 4) % M)
i += 1
}
@@ -45,23 +76,15 @@ class MessageBufferTest extends MessagePackSpec {
time("sequential getInt", repeat = rep) {
block("unsafe array") {
var i = 0
- while(i < N) {
+ while (i < N) {
ub.getInt((i * 4) % M)
i += 1
}
}
- block("unsafe direct") {
- var i = 0
- while(i < N) {
- ud.getInt((i * 4) % M)
- i += 1
- }
- }
-
block("allocate") {
var i = 0
- while(i < N) {
+ while (i < N) {
hb.getInt((i * 4) % M)
i += 1
}
@@ -69,7 +92,7 @@ class MessageBufferTest extends MessagePackSpec {
block("allocateDirect") {
var i = 0
- while(i < N) {
+ while (i < N) {
db.getInt((i * 4) % M)
i += 1
}
@@ -79,23 +102,15 @@ class MessageBufferTest extends MessagePackSpec {
time("random getInt", repeat = rep) {
block("unsafe array") {
var i = 0
- while(i < N) {
+ while (i < N) {
ub.getInt((rs(i) * 4) % M)
i += 1
}
}
- block("unsafe direct") {
- var i = 0
- while(i < N) {
- ud.getInt((rs(i) * 4) % M)
- i += 1
- }
- }
-
block("allocate") {
var i = 0
- while(i < N) {
+ while (i < N) {
hb.getInt((rs(i) * 4) % M)
i += 1
}
@@ -103,17 +118,81 @@ class MessageBufferTest extends MessagePackSpec {
block("allocateDirect") {
var i = 0
- while(i < N) {
+ while (i < N) {
db.getInt((rs(i) * 4) % M)
i += 1
}
}
}
+ }
+ "convert to ByteBuffer" in {
+ for (t <- Seq(
+ MessageBuffer.allocate(10))
+ ) {
+ val bb = t.sliceAsByteBuffer
+ bb.position shouldBe 0
+ bb.limit shouldBe 10
+ bb.capacity shouldBe 10
+ }
}
- }
+ "put ByteBuffer on itself" in {
+ for (t <- Seq(
+ MessageBuffer.allocate(10))
+ ) {
+ val b = Array[Byte](0x02, 0x03)
+ val srcArray = ByteBuffer.wrap(b)
+ val srcHeap = ByteBuffer.allocate(b.length)
+ srcHeap.put(b).flip
+ val srcOffHeap = ByteBuffer.allocateDirect(b.length)
+ srcOffHeap.put(b).flip
+
+ for (src <- Seq(srcArray, srcHeap, srcOffHeap)) {
+ // Write header bytes
+ val header = Array[Byte](0x00, 0x01)
+ t.putBytes(0, header, 0, header.length)
+ // Write src after the header
+ t.putByteBuffer(header.length, src, header.length)
+
+ t.getByte(0) shouldBe 0x00
+ t.getByte(1) shouldBe 0x01
+ t.getByte(2) shouldBe 0x02
+ t.getByte(3) shouldBe 0x03
+ }
+ }
+ }
+
+ "copy sliced buffer" in {
+ def prepareBytes : Array[Byte] = {
+ Array[Byte](0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07)
+ }
+ def checkSliceAndCopyTo(srcBuffer: MessageBuffer, dstBuffer: MessageBuffer) = {
+ val sliced = srcBuffer.slice(2, 5)
+
+ sliced.size() shouldBe 5
+ sliced.getByte(0) shouldBe 0x02
+ sliced.getByte(1) shouldBe 0x03
+ sliced.getByte(2) shouldBe 0x04
+ sliced.getByte(3) shouldBe 0x05
+ sliced.getByte(4) shouldBe 0x06
+
+ sliced.copyTo(3, dstBuffer, 1, 2) // copy 0x05 and 0x06 to dstBuffer[1] and [2]
+
+ dstBuffer.getByte(0) shouldBe 0x00
+ dstBuffer.getByte(1) shouldBe 0x05 // copied by sliced.getByte(3)
+ dstBuffer.getByte(2) shouldBe 0x06 // copied by sliced.getByte(4)
+ dstBuffer.getByte(3) shouldBe 0x03
+ dstBuffer.getByte(4) shouldBe 0x04
+ dstBuffer.getByte(5) shouldBe 0x05
+ dstBuffer.getByte(6) shouldBe 0x06
+ dstBuffer.getByte(7) shouldBe 0x07
+ }
+
+ checkSliceAndCopyTo(MessageBuffer.wrap(prepareBytes), MessageBuffer.wrap(prepareBytes))
+ }
+ }
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/core/example/MessagePackExampleTest.scala b/msgpack-core/src/test/scala/org/msgpack/core/example/MessagePackExampleTest.scala
index 5f4bc728c..bacf39ed4 100644
--- a/msgpack-core/src/test/scala/org/msgpack/core/example/MessagePackExampleTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/core/example/MessagePackExampleTest.scala
@@ -1,12 +1,27 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.core.example
import org.msgpack.core.MessagePackSpec
-import org.scalatest.FunSuite
/**
*
*/
-class MessagePackExampleTest extends MessagePackSpec {
+class MessagePackExampleTest
+ extends MessagePackSpec {
"example" should {
@@ -25,6 +40,5 @@ class MessagePackExampleTest extends MessagePackSpec {
"have configuration example" in {
MessagePackExample.configuration();
}
-
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/CursorTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/CursorTest.scala
deleted file mode 100644
index ee46ff7bd..000000000
--- a/msgpack-core/src/test/scala/org/msgpack/value/CursorTest.scala
+++ /dev/null
@@ -1,199 +0,0 @@
-//
-// MessagePack for Java
-//
-// Licensed 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 org.msgpack.value
-
-import java.io.ByteArrayInputStream
-
-import org.msgpack.core.{MessagePack, MessageUnpacker, MessagePackSpec}
-import ValueFactory._
-import scala.util.Random
-import org.msgpack.value.holder.{ValueHolder, IntegerHolder}
-
-/**
- * Created on 6/13/14.
- */
-class CursorTest extends MessagePackSpec {
-
- val msgpack = MessagePack.DEFAULT
-
- def sampleData = createMessagePackData { packer =>
- packer.packValue(
- ValueFactory.newArray(
- newInt(10),
- newBinary("message pack".getBytes(MessagePack.UTF8)),
- newString("hello")
- )
- )
- }
-
- def intSeq(n:Int) = createMessagePackData { packer =>
- (0 until n).foreach { i =>
- packer.packInt(Random.nextInt(65536))
- }
- }
- def binSeq(n:Int) = createMessagePackData { packer =>
- (0 until n).foreach { i =>
- val len = Random.nextInt(256)
- val b = new Array[Byte](len)
- Random.nextBytes(b)
- packer.packBinaryHeader(b.length).writePayload(b)
- }
- }
-
-
- "Cursor" should {
-
- "have array cursor" taggedAs("array") in {
-
- val cursor = msgpack.newUnpacker(sampleData).getCursor
- // Traverse as references
- val arrCursor = cursor.nextRef().getArrayCursor
- arrCursor.size() shouldBe 3
-
- import scala.collection.JavaConversions._
- for(v <- arrCursor) {
- info(s"[${v.getValueType}]\t${v}")
- }
- }
-
- "have map cursor" taggedAs("map") in {
- val packedData = createMessagePackData { packer =>
- packer packMapHeader(1) packString("f") packString("x")
- }
-
- val cursor = msgpack.newUnpacker(packedData).getCursor
- val mapCursor = cursor.nextRef().getMapCursor
- mapCursor.size() shouldBe 1
-
- val mapValue = mapCursor.toValue
- val data = mapValue.toKeyValueSeq
-
- data should have length 2
-
- data(0).asString().toString shouldBe "f"
- data(1).asString().toString shouldBe "x"
- }
-
- "traverse ValueRef faster than traversing Value" taggedAs("ref") in {
- val N = 10000
- val data = binSeq(N)
-
- time("traversal", repeat=100) {
- block("value") {
- val cursor = msgpack.newUnpacker(data).getCursor
- while(cursor.hasNext) {
- cursor.next()
- }
- cursor.close()
- }
- block("value-ref") {
- val cursor = msgpack.newUnpacker(data).getCursor
- while(cursor.hasNext) {
- cursor.nextRef()
- }
- cursor.close()
- }
- }
-
- }
-
- "have negligible overhead" taggedAs("perf") in {
- val N = 10000
- val data = intSeq(N)
- time("scan int-seq", repeat=1000) {
- block("unpacker") {
- val unpacker = msgpack.newUnpacker(data)
- val intHolder = new IntegerHolder()
- var count = 0
- while(unpacker.hasNext) {
- val vt = unpacker.getNextFormat.getValueType
- if(vt.isIntegerType) {
- unpacker.unpackInteger(intHolder);
- count += 1
- }
- else {
- throw new IllegalStateException(s"invalid format: ${vt}")
- }
- }
- unpacker.close()
- count shouldBe N
- }
- block("cursor") {
- var count = 0
- val cursor = msgpack.newUnpacker(data).getCursor
- while(cursor.hasNext) {
- val ref = cursor.nextRef()
- val v = ref.asInteger().toInt
- count += 1
- }
- cursor.close()
- count shouldBe N
- }
- }
-
- }
-
- "create immutable map" taggedAs("im-map") in {
-
- val m = createMessagePackData { packer =>
- packer.packMapHeader(3)
-
- // A -> [1, "leo"]
- packer.packString("A")
- packer.packArrayHeader(2)
- packer.packInt(1)
- packer.packString("leo")
-
- // B -> 10
- packer.packString("B")
- packer.packInt(10)
-
- // C -> {a -> 1.0f, b -> 5, c -> {cc->1}}
- packer.packString("C")
- packer.packMapHeader(3)
- packer.packString("a")
- packer.packFloat(1.0f)
- packer.packString("b")
- packer.packInt(5)
-
- packer.packString("c")
- packer.packMapHeader(1)
- packer.packString("cc")
- packer.packInt(1)
-
- }
-
- val unpacker = msgpack.newUnpacker(m)
- val vh = new ValueHolder
- unpacker.unpackValue(vh)
- val mapValue = vh.get().asMapValue()
-
- val map = mapValue.toMap
- map.size shouldBe 3
-
- val arr = map.get(ValueFactory.newString("A")).asArrayValue()
- arr.size shouldBe 2
-
- val cmap = map.get(ValueFactory.newString("C")).asMapValue()
- cmap.size shouldBe 3
- cmap.toMap.get(ValueFactory.newString("c")).asMapValue().size() shouldBe 1
-
- info(mapValue)
- }
-
-
- }
-}
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/RawStringValueImplTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/RawStringValueImplTest.scala
new file mode 100644
index 000000000..e545d7d2a
--- /dev/null
+++ b/msgpack-core/src/test/scala/org/msgpack/value/RawStringValueImplTest.scala
@@ -0,0 +1,35 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value
+
+import org.msgpack.core.MessagePackSpec
+
+class RawStringValueImplTest
+ extends MessagePackSpec {
+
+ "StringValue" should {
+ "return the same hash code if they are equal" in {
+ val str = "a"
+ val a1 = ValueFactory.newString(str.getBytes("UTF-8"))
+ val a2 = ValueFactory.newString(str)
+
+ a1.shouldEqual(a2)
+ a1.hashCode.shouldEqual(a2.hashCode)
+ a2.shouldEqual(a1)
+ a2.hashCode.shouldEqual(a1.hashCode)
+ }
+ }
+}
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/ValueFactoryTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/ValueFactoryTest.scala
index cb79a575e..a8d996376 100644
--- a/msgpack-core/src/test/scala/org/msgpack/value/ValueFactoryTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/value/ValueFactoryTest.scala
@@ -1,53 +1,69 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value
-import org.scalatest.FunSuite
import org.msgpack.core.MessagePackSpec
/**
- * Created on 6/13/14.
+ *
*/
-class ValueFactoryTest extends MessagePackSpec {
+class ValueFactoryTest
+ extends MessagePackSpec {
- def isValid(v:Value,
- expected:ValueType,
+ def isValid(v: Value,
+ expected: ValueType,
isNil: Boolean = false,
isBoolean: Boolean = false,
isInteger: Boolean = false,
- isString : Boolean = false,
+ isString: Boolean = false,
isFloat: Boolean = false,
isBinary: Boolean = false,
isArray: Boolean = false,
isMap: Boolean = false,
- isExtended : Boolean = false,
- isRaw : Boolean = false,
- isNumber : Boolean = false
+ isExtension: Boolean = false,
+ isRaw: Boolean = false,
+ isNumber: Boolean = false
) {
- v.isNil shouldBe isNil
- v.isBoolean shouldBe isBoolean
- v.isInteger shouldBe isInteger
- v.isFloat shouldBe isFloat
- v.isString shouldBe isString
- v.isBinary shouldBe isBinary
- v.isArray shouldBe isArray
- v.isMap shouldBe isMap
- v.isExtended shouldBe isExtended
- v.isRaw shouldBe isRaw
- v.isNumber shouldBe isNumber
+ v.isNilValue shouldBe isNil
+ v.isBooleanValue shouldBe isBoolean
+ v.isIntegerValue shouldBe isInteger
+ v.isFloatValue shouldBe isFloat
+ v.isStringValue shouldBe isString
+ v.isBinaryValue shouldBe isBinary
+ v.isArrayValue shouldBe isArray
+ v.isMapValue shouldBe isMap
+ v.isExtensionValue shouldBe isExtension
+ v.isRawValue shouldBe isRaw
+ v.isNumberValue shouldBe isNumber
}
"ValueFactory" should {
"create valid type values" in {
- isValid(ValueFactory.nilValue(), expected=ValueType.NIL, isNil = true)
- forAll{(v:Boolean) => isValid(ValueFactory.newBoolean(v), expected=ValueType.BOOLEAN, isBoolean = true)}
- forAll{(v:Int) => isValid(ValueFactory.newInt(v), expected=ValueType.INTEGER, isInteger = true, isNumber = true)}
- forAll{(v:Float) => isValid(ValueFactory.newFloat(v), expected=ValueType.FLOAT, isFloat = true, isNumber = true)}
- forAll{(v:String) => isValid(ValueFactory.newString(v), expected=ValueType.STRING, isString = true, isRaw = true)}
- forAll{(v:Array[Byte]) => isValid(ValueFactory.newBinary(v), expected=ValueType.BINARY, isBinary = true, isRaw = true)}
- isValid(ValueFactory.emptyArray(), expected=ValueType.ARRAY, isArray = true)
- isValid(ValueFactory.emptyMap(), expected=ValueType.MAP, isMap = true)
- forAll{(v:Array[Byte]) => isValid(ValueFactory.newExtendedValue(0, v), expected=ValueType.EXTENDED, isExtended=true, isRaw=true)}
+ isValid(ValueFactory.newNil(), expected = ValueType.NIL, isNil = true)
+ forAll { (v: Boolean) => isValid(ValueFactory.newBoolean(v), expected = ValueType.BOOLEAN, isBoolean = true) }
+ forAll { (v: Int) => isValid(ValueFactory.newInteger(v), expected = ValueType.INTEGER, isInteger = true, isNumber = true) }
+ forAll { (v: Float) => isValid(ValueFactory.newFloat(v), expected = ValueType.FLOAT, isFloat = true, isNumber = true) }
+ forAll { (v: String) => isValid(ValueFactory.newString(v), expected = ValueType.STRING, isString = true, isRaw = true) }
+ forAll { (v: Array[Byte]) => isValid(ValueFactory.newBinary(v), expected = ValueType.BINARY, isBinary = true, isRaw = true) }
+ isValid(ValueFactory.emptyArray(), expected = ValueType.ARRAY, isArray = true)
+ isValid(ValueFactory.emptyMap(), expected = ValueType.MAP, isMap = true)
+ forAll { (v: Array[Byte]) => isValid(ValueFactory.newExtension(0, v), expected = ValueType
+ .EXTENSION, isExtension = true, isRaw = true)
+ }
}
-
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/ValueTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/ValueTest.scala
new file mode 100644
index 000000000..6cb7af603
--- /dev/null
+++ b/msgpack-core/src/test/scala/org/msgpack/value/ValueTest.scala
@@ -0,0 +1,130 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value
+
+import java.math.BigInteger
+import org.msgpack.core._
+
+import scala.util.parsing.json.JSON
+
+class ValueTest extends MessagePackSpec
+{
+ def checkSuccinctType(pack:MessagePacker => Unit, expectedAtMost:MessageFormat) {
+ val b = createMessagePackData(pack)
+ val v1 = MessagePack.newDefaultUnpacker(b).unpackValue()
+ val mf = v1.asIntegerValue().mostSuccinctMessageFormat()
+ mf.getValueType shouldBe ValueType.INTEGER
+ mf.ordinal() shouldBe <= (expectedAtMost.ordinal())
+
+ val v2 = new Variable
+ MessagePack.newDefaultUnpacker(b).unpackValue(v2)
+ val mf2 = v2.asIntegerValue().mostSuccinctMessageFormat()
+ mf2.getValueType shouldBe ValueType.INTEGER
+ mf2.ordinal() shouldBe <= (expectedAtMost.ordinal())
+ }
+
+ "Value" should {
+ "tell most succinct integer type" in {
+ forAll { (v: Byte) => checkSuccinctType(_.packByte(v), MessageFormat.INT8) }
+ forAll { (v: Short) => checkSuccinctType(_.packShort(v), MessageFormat.INT16) }
+ forAll { (v: Int) => checkSuccinctType(_.packInt(v), MessageFormat.INT32) }
+ forAll { (v: Long) => checkSuccinctType(_.packLong(v), MessageFormat.INT64) }
+ forAll { (v: Long) => checkSuccinctType(_.packBigInteger(BigInteger.valueOf(v)), MessageFormat.INT64) }
+ forAll { (v: Long) =>
+ whenever(v > 0) {
+ // Create value between 2^63-1 < v <= 2^64-1
+ checkSuccinctType(_.packBigInteger(BigInteger.valueOf(Long.MaxValue).add(BigInteger.valueOf(v))), MessageFormat.UINT64)
+ }
+ }
+ }
+
+ "produce json strings" in {
+
+ import ValueFactory._
+
+ newNil().toJson shouldBe "null"
+ newNil().toString shouldBe "null"
+
+ newBoolean(true).toJson shouldBe "true"
+ newBoolean(false).toJson shouldBe "false"
+ newBoolean(true).toString shouldBe "true"
+ newBoolean(false).toString shouldBe "false"
+
+ newInteger(3).toJson shouldBe "3"
+ newInteger(3).toString shouldBe "3"
+ newInteger(BigInteger.valueOf(1324134134134L)).toJson shouldBe "1324134134134"
+ newInteger(BigInteger.valueOf(1324134134134L)).toString shouldBe "1324134134134"
+
+ newFloat(0.1).toJson shouldBe "0.1"
+ newFloat(0.1).toString shouldBe "0.1"
+
+ newArray(newInteger(0), newString("hello")).toJson shouldBe "[0,\"hello\"]"
+ newArray(newInteger(0), newString("hello")).toString shouldBe "[0,\"hello\"]"
+ newArray(newArray(newString("Apple"), newFloat(0.2)), newNil()).toJson shouldBe """[["Apple",0.2],null]"""
+
+ // Map value
+ val m = newMapBuilder()
+ .put(newString("id"), newInteger(1001))
+ .put(newString("name"), newString("leo"))
+ .put(newString("address"), newArray(newString("xxx-xxxx"), newString("yyy-yyyy")))
+ .put(newString("name"), newString("mitsu"))
+ .build()
+ val i1 = JSON.parseFull(m.toJson)
+ val i2 = JSON.parseFull(m.toString) // expect json value
+ val a1 = JSON.parseFull("""{"id":1001,"name":"mitsu","address":["xxx-xxxx","yyy-yyyy"]}""")
+ // Equals as JSON map
+ i1 shouldBe a1
+ i2 shouldBe a1
+
+ // toJson should quote strings
+ newString("1").toJson shouldBe "\"1\""
+ // toString is for extracting string values
+ newString("1").toString shouldBe "1"
+
+ }
+
+ "check appropriate range for integers" in {
+ import ValueFactory._
+ import java.lang.Byte
+ import java.lang.Short
+
+ newInteger(Byte.MAX_VALUE).asByte() shouldBe Byte.MAX_VALUE
+ newInteger(Byte.MIN_VALUE).asByte() shouldBe Byte.MIN_VALUE
+ newInteger(Short.MAX_VALUE).asShort() shouldBe Short.MAX_VALUE
+ newInteger(Short.MIN_VALUE).asShort() shouldBe Short.MIN_VALUE
+ newInteger(Integer.MAX_VALUE).asInt() shouldBe Integer.MAX_VALUE
+ newInteger(Integer.MIN_VALUE).asInt() shouldBe Integer.MIN_VALUE
+ intercept[MessageIntegerOverflowException] {
+ newInteger(Byte.MAX_VALUE+1).asByte()
+ }
+ intercept[MessageIntegerOverflowException] {
+ newInteger(Byte.MIN_VALUE-1).asByte()
+ }
+ intercept[MessageIntegerOverflowException] {
+ newInteger(Short.MAX_VALUE+1).asShort()
+ }
+ intercept[MessageIntegerOverflowException] {
+ newInteger(Short.MIN_VALUE-1).asShort()
+ }
+ intercept[MessageIntegerOverflowException] {
+ newInteger(Integer.MAX_VALUE+1.toLong).asInt()
+ }
+ intercept[MessageIntegerOverflowException] {
+ newInteger(Integer.MIN_VALUE-1.toLong).asInt()
+ }
+ }
+ }
+}
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/ValueTypeTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/ValueTypeTest.scala
index 467162447..979c33c9b 100644
--- a/msgpack-core/src/test/scala/org/msgpack/value/ValueTypeTest.scala
+++ b/msgpack-core/src/test/scala/org/msgpack/value/ValueTypeTest.scala
@@ -1,109 +1,92 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.value
-import scala.util.Random
-import org.msgpack.core.MessagePack.Code
-import org.msgpack.core.{MessageFormat, MessageFormatException, MessagePackSpec}
import org.msgpack.core.MessagePack.Code._
-
+import org.msgpack.core.{MessageFormat, MessageFormatException, MessagePackSpec}
/**
- * Created on 2014/05/06.
- */
-class ValueTypeTest extends MessagePackSpec {
+ * Created on 2014/05/06.
+ */
+class ValueTypeTest
+ extends MessagePackSpec
+{
"ValueType" should {
- "lookup ValueType from a byte value" taggedAs("code") in {
+ "lookup ValueType from a byte value" taggedAs ("code") in {
- def check(b:Byte, tpe:ValueType) {
- ValueType.valueOf(b) shouldBe tpe
+ def check(b: Byte, tpe: ValueType)
+ {
+ MessageFormat.valueOf(b).getValueType shouldBe tpe
}
- for(i <- 0 until 0x7f)
+ for (i <- 0 until 0x7f) {
check(i.toByte, ValueType.INTEGER)
+ }
- for(i <- 0x80 until 0x8f)
+ for (i <- 0x80 until 0x8f) {
check(i.toByte, ValueType.MAP)
+ }
- for(i <- 0x90 until 0x9f)
+ for (i <- 0x90 until 0x9f) {
check(i.toByte, ValueType.ARRAY)
+ }
check(NIL, ValueType.NIL)
try {
- ValueType.valueOf(NEVER_USED)
+ MessageFormat.valueOf(NEVER_USED).getValueType
fail("NEVER_USED type should not have ValueType")
}
catch {
- case e:MessageFormatException =>
- // OK
+ case e: MessageFormatException =>
+ // OK
}
check(TRUE, ValueType.BOOLEAN)
check(FALSE, ValueType.BOOLEAN)
- for(t <- Seq(BIN8, BIN16, BIN32))
+ for (t <- Seq(BIN8, BIN16, BIN32)) {
check(t, ValueType.BINARY)
+ }
- for(t <- Seq(FIXEXT1, FIXEXT2, FIXEXT4, FIXEXT8, FIXEXT16, EXT8, EXT16, EXT32))
- check(t, ValueType.EXTENDED)
+ for (t <- Seq(FIXEXT1, FIXEXT2, FIXEXT4, FIXEXT8, FIXEXT16, EXT8, EXT16, EXT32)) {
+ check(t, ValueType.EXTENSION)
+ }
- for(t <- Seq(INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64))
+ for (t <- Seq(INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64)) {
check(t, ValueType.INTEGER)
+ }
- for(t <- Seq(STR8, STR16, STR32))
+ for (t <- Seq(STR8, STR16, STR32)) {
check(t, ValueType.STRING)
+ }
- for(t <- Seq(FLOAT32, FLOAT64))
+ for (t <- Seq(FLOAT32, FLOAT64)) {
check(t, ValueType.FLOAT)
-
- for(t <- Seq(ARRAY16, ARRAY32))
- check(t, ValueType.ARRAY)
-
- for(i <- 0xe0 until 0xff)
- check(i.toByte, ValueType.INTEGER)
-
- }
-
- "lookup table" in {
-
- val N = 100000
- val idx = {
- val b = Array.newBuilder[Byte]
- for(i <- 0 until N) {
- val r = Iterator.continually(Random.nextInt(256)).find(_.toByte != Code.NEVER_USED).get
- b += r.toByte
- }
- b.result()
}
- time("lookup", repeat=100) {
- block("switch") {
- var i = 0
- while(i < N) {
- MessageFormat.toMessageFormat(idx(i)).getValueType()
- i += 1
- }
- }
-
- block("table") {
- var i = 0
- while(i < N) {
- ValueType.valueOf(idx(i))
- i += 1
- }
- }
-
+ for (t <- Seq(ARRAY16, ARRAY32)) {
+ check(t, ValueType.ARRAY)
}
- }
-
- "support isTypeOf" in {
- for(v <- ValueType.values()) {
- v.isTypeOf(v.getBitMask) shouldBe true
+ for (i <- 0xe0 until 0xff) {
+ check(i.toByte, ValueType.INTEGER)
}
}
-
-
}
}
diff --git a/msgpack-core/src/test/scala/org/msgpack/value/holder/FloatHolderTest.scala b/msgpack-core/src/test/scala/org/msgpack/value/holder/FloatHolderTest.scala
deleted file mode 100644
index 126d39352..000000000
--- a/msgpack-core/src/test/scala/org/msgpack/value/holder/FloatHolderTest.scala
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.msgpack.value.holder
-
-import org.msgpack.core.MessagePackSpec
-
-/**
- *
- */
-class FloatHolderTest extends MessagePackSpec {
-
- "FloatHolder" should {
-
- "display value in an appropriate format" in {
-
- val h = new FloatHolder
- val f = 0.1341f
- h.setFloat(f)
- h.toString shouldBe java.lang.Float.toString(f)
-
- val d = 0.1341341344
- h.setDouble(d)
- h.toString shouldBe java.lang.Double.toString(d)
- }
-
- }
-
-}
diff --git a/msgpack-jackson/README.md b/msgpack-jackson/README.md
index 138b4dcec..51435b401 100644
--- a/msgpack-jackson/README.md
+++ b/msgpack-jackson/README.md
@@ -1,26 +1,38 @@
# jackson-dataformat-msgpack
This Jackson extension library handles reading and writing of data encoded in [MessagePack](http://msgpack.org/) data format.
-It extends standard Jackson streaming API (`JsonFactory`, `JsonParser`, `JsonGenerator`), and as such works seamlessly with all the higher level data abstractions (data binding, tree model, and pluggable extensions).
+It extends standard Jackson streaming API (`JsonFactory`, `JsonParser`, `JsonGenerator`), and as such works seamlessly with all the higher level data abstractions (data binding, tree model, and pluggable extensions). For the details of Jackson-annotations, please see https://github.com/FasterXML/jackson-annotations.
-## Maven dependency
+## Install
-To use this module on Maven-based projects, use following dependency:
+### Maven
```
org.msgpack
jackson-dataformat-msgpack
- 0.7.0-M5
+ 0.7.1
```
+### Gradle
+```
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compile 'org.msgpack:jackson-dataformat-msgpack:0.7.1'
+}
+```
+
+
## Usage
-Only thing you need to do is to instantiate MessagePackFactory and pass it to the constructor of ObjectMapper.
+Only thing you need to do is to instantiate MessagePackFormatFactory and pass it to the constructor of ObjectMapper.
```
- ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ ObjectMapper objectMapper = new ObjectMapper(new MessagePackFormatFactory());
ExamplePojo orig = new ExamplePojo("komamitsu");
byte[] bytes = objectMapper.writeValueAsBytes(orig);
ExamplePojo value = objectMapper.readValue(bytes, ExamplePojo.class);
diff --git a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackExtensionType.java b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackExtensionType.java
new file mode 100644
index 000000000..1906757f5
--- /dev/null
+++ b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackExtensionType.java
@@ -0,0 +1,48 @@
+package org.msgpack.jackson.dataformat;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import java.io.IOException;
+
+@JsonSerialize(using = MessagePackExtensionType.Serializer.class)
+public class MessagePackExtensionType
+{
+ private final byte type;
+ private final byte[] data;
+
+ public MessagePackExtensionType(byte type, byte[] data)
+ {
+ this.type = type;
+ this.data = data;
+ }
+
+ public byte getType()
+ {
+ return type;
+ }
+
+ public byte[] getData()
+ {
+ return data;
+ }
+
+ public static class Serializer extends JsonSerializer
+ {
+ @Override
+ public void serialize(MessagePackExtensionType value, JsonGenerator gen, SerializerProvider serializers)
+ throws IOException, JsonProcessingException
+ {
+ if (gen instanceof MessagePackGenerator) {
+ MessagePackGenerator msgpackGenerator = (MessagePackGenerator) gen;
+ msgpackGenerator.writeExtensionType(value);
+ }
+ else {
+ throw new IllegalStateException("'gen' is expected to be MessagePackGenerator but it's " + gen.getClass());
+ }
+ }
+ }
+}
diff --git a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackFactory.java b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackFactory.java
index 9318d0523..ff7aa373f 100644
--- a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackFactory.java
+++ b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackFactory.java
@@ -15,48 +15,79 @@
//
package org.msgpack.jackson.dataformat;
-import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.io.IOContext;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.Writer;
import java.util.Arrays;
-public class MessagePackFactory extends JsonFactory {
+public class MessagePackFactory
+ extends JsonFactory
+{
private static final long serialVersionUID = 2578263992015504347L;
- protected int messagePackGeneratorFeature = 0;
- protected int messagePackParserFeature = 0;
@Override
- public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException {
- return new MessagePackGenerator(messagePackGeneratorFeature, _objectCodec, out);
+ public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc)
+ throws IOException
+ {
+ return new MessagePackGenerator(_generatorFeatures, _objectCodec, out);
}
@Override
- public JsonParser createParser(byte[] data) throws IOException, JsonParseException {
+ public JsonGenerator createGenerator(File f, JsonEncoding enc)
+ throws IOException
+ {
+ return createGenerator(new FileOutputStream(f), enc);
+ }
+
+ @Override
+ public JsonGenerator createGenerator(Writer w)
+ throws IOException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public JsonParser createParser(byte[] data)
+ throws IOException, JsonParseException
+ {
IOContext ioContext = _createContext(data, false);
return _createParser(data, 0, data.length, ioContext);
}
@Override
- public JsonParser createParser(InputStream in) throws IOException, JsonParseException {
+ public JsonParser createParser(InputStream in)
+ throws IOException, JsonParseException
+ {
IOContext ioContext = _createContext(in, false);
return _createParser(in, ioContext);
}
@Override
- protected MessagePackParser _createParser(InputStream in, IOContext ctxt) throws IOException {
- MessagePackParser parser = new MessagePackParser(ctxt, messagePackParserFeature, in);
+ protected MessagePackParser _createParser(InputStream in, IOContext ctxt)
+ throws IOException
+ {
+ MessagePackParser parser = new MessagePackParser(ctxt, _parserFeatures, _objectCodec, in);
return parser;
}
@Override
- protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException, JsonParseException {
+ protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt)
+ throws IOException, JsonParseException
+ {
if (offset != 0 || len != data.length) {
data = Arrays.copyOfRange(data, offset, offset + len);
}
- MessagePackParser parser = new MessagePackParser(ctxt, messagePackParserFeature, data);
+ MessagePackParser parser = new MessagePackParser(ctxt, _parserFeatures, _objectCodec, data);
return parser;
}
}
diff --git a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackGenerator.java b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackGenerator.java
index c6bc3551e..e62528a75 100644
--- a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackGenerator.java
+++ b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackGenerator.java
@@ -1,3 +1,18 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.jackson.dataformat;
import com.fasterxml.jackson.core.Base64Variant;
@@ -5,6 +20,7 @@
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.base.GeneratorBase;
import com.fasterxml.jackson.core.json.JsonWriteContext;
+import org.msgpack.core.MessagePack;
import org.msgpack.core.MessagePacker;
import org.msgpack.core.buffer.OutputStreamBufferOutput;
@@ -18,56 +34,70 @@
import java.util.LinkedList;
import java.util.List;
-public class MessagePackGenerator extends GeneratorBase {
+public class MessagePackGenerator
+ extends GeneratorBase
+{
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private static ThreadLocal messagePackersHolder = new ThreadLocal();
private static ThreadLocal messageBufferOutputHolder = new ThreadLocal();
private LinkedList stack;
private StackItem rootStackItem;
-
- private static abstract class StackItem {
+ private abstract static class StackItem
+ {
protected List objectKeys = new ArrayList();
protected List objectValues = new ArrayList();
abstract void addKey(String key);
- void addValue(Object value) {
+ void addValue(Object value)
+ {
objectValues.add(value);
}
abstract List getKeys();
- List getValues() {
+ List getValues()
+ {
return objectValues;
}
}
- private static class StackItemForObject extends StackItem {
+ private static class StackItemForObject
+ extends StackItem
+ {
@Override
- void addKey(String key) {
+ void addKey(String key)
+ {
objectKeys.add(key);
}
@Override
- List getKeys() {
+ List getKeys()
+ {
return objectKeys;
}
}
- private static class StackItemForArray extends StackItem {
+ private static class StackItemForArray
+ extends StackItem
+ {
@Override
- void addKey(String key) {
+ void addKey(String key)
+ {
throw new IllegalStateException("This method shouldn't be called");
}
@Override
- List getKeys() {
+ List getKeys()
+ {
throw new IllegalStateException("This method shouldn't be called");
}
}
- public MessagePackGenerator(int features, ObjectCodec codec, OutputStream out) throws IOException {
+ public MessagePackGenerator(int features, ObjectCodec codec, OutputStream out)
+ throws IOException
+ {
super(features, codec);
MessagePacker messagePacker = messagePackersHolder.get();
OutputStreamBufferOutput messageBufferOutput = messageBufferOutputHolder.get();
@@ -80,7 +110,7 @@ public MessagePackGenerator(int features, ObjectCodec codec, OutputStream out) t
messageBufferOutputHolder.set(messageBufferOutput);
if (messagePacker == null) {
- messagePacker = new MessagePacker(messageBufferOutput);
+ messagePacker = MessagePack.newDefaultPacker(messageBufferOutput);
}
else {
messagePacker.reset(messageBufferOutput);
@@ -91,13 +121,17 @@ public MessagePackGenerator(int features, ObjectCodec codec, OutputStream out) t
}
@Override
- public void writeStartArray() throws IOException, JsonGenerationException {
+ public void writeStartArray()
+ throws IOException, JsonGenerationException
+ {
_writeContext = _writeContext.createChildArrayContext();
stack.push(new StackItemForArray());
}
@Override
- public void writeEndArray() throws IOException, JsonGenerationException {
+ public void writeEndArray()
+ throws IOException, JsonGenerationException
+ {
if (!_writeContext.inArray()) {
_reportError("Current context not an array but " + _writeContext.getTypeDesc());
}
@@ -110,13 +144,17 @@ public void writeEndArray() throws IOException, JsonGenerationException {
}
@Override
- public void writeStartObject() throws IOException, JsonGenerationException {
+ public void writeStartObject()
+ throws IOException, JsonGenerationException
+ {
_writeContext = _writeContext.createChildObjectContext();
stack.push(new StackItemForObject());
}
@Override
- public void writeEndObject() throws IOException, JsonGenerationException {
+ public void writeEndObject()
+ throws IOException, JsonGenerationException
+ {
if (!_writeContext.inObject()) {
_reportError("Current context not an object but " + _writeContext.getTypeDesc());
}
@@ -134,7 +172,9 @@ public void writeEndObject() throws IOException, JsonGenerationException {
popStackAndStoreTheItemAsValue();
}
- private void packValue(Object v) throws IOException {
+ private void packValue(Object v)
+ throws IOException
+ {
MessagePacker messagePacker = getMessagePacker();
if (v == null) {
messagePacker.packNil();
@@ -144,8 +184,17 @@ else if (v instanceof Integer) {
}
else if (v instanceof ByteBuffer) {
ByteBuffer bb = (ByteBuffer) v;
- messagePacker.packBinaryHeader(bb.limit());
- messagePacker.writePayload(bb);
+ int len = bb.remaining();
+ if (bb.hasArray()) {
+ messagePacker.packBinaryHeader(len);
+ messagePacker.writePayload(bb.array(), bb.arrayOffset(), len);
+ }
+ else {
+ byte[] data = new byte[len];
+ bb.get(data);
+ messagePacker.packBinaryHeader(len);
+ messagePacker.addPayload(data);
+ }
}
else if (v instanceof String) {
messagePacker.packString((String) v);
@@ -169,18 +218,52 @@ else if (v instanceof BigInteger) {
messagePacker.packBigInteger((BigInteger) v);
}
else if (v instanceof BigDecimal) {
- // TODO
- throw new UnsupportedOperationException("BigDecimal isn't supported yet");
+ packBigDecimal((BigDecimal) v);
}
else if (v instanceof Boolean) {
messagePacker.packBoolean((Boolean) v);
}
+ else if (v instanceof MessagePackExtensionType) {
+ MessagePackExtensionType extensionType = (MessagePackExtensionType) v;
+ byte[] extData = extensionType.getData();
+ messagePacker.packExtensionTypeHeader(extensionType.getType(), extData.length);
+ messagePacker.writePayload(extData);
+ }
else {
throw new IllegalArgumentException(v.toString());
}
}
- private void packObject(StackItemForObject stackItem) throws IOException {
+ private void packBigDecimal(BigDecimal decimal)
+ throws IOException
+ {
+ MessagePacker messagePacker = getMessagePacker();
+ boolean failedToPackAsBI = false;
+ try {
+ //Check to see if this BigDecimal can be converted to BigInteger
+ BigInteger integer = decimal.toBigIntegerExact();
+ messagePacker.packBigInteger(integer);
+ }
+ catch (ArithmeticException e) {
+ failedToPackAsBI = true;
+ }
+ catch (IllegalArgumentException e) {
+ failedToPackAsBI = true;
+ }
+
+ if (failedToPackAsBI) {
+ double doubleValue = decimal.doubleValue();
+ //Check to make sure this BigDecimal can be represented as a double
+ if (!decimal.stripTrailingZeros().toEngineeringString().equals(BigDecimal.valueOf(doubleValue).toEngineeringString())) {
+ throw new IllegalArgumentException("MessagePack cannot serialize a BigDecimal that can't be represented as double. " + decimal);
+ }
+ messagePacker.packDouble(doubleValue);
+ }
+ }
+
+ private void packObject(StackItemForObject stackItem)
+ throws IOException
+ {
List keys = stackItem.getKeys();
List values = stackItem.getValues();
@@ -194,7 +277,9 @@ private void packObject(StackItemForObject stackItem) throws IOException {
}
}
- private void packArray(StackItemForArray stackItem) throws IOException {
+ private void packArray(StackItemForArray stackItem)
+ throws IOException
+ {
List values = stackItem.getValues();
MessagePacker messagePacker = getMessagePacker();
@@ -207,116 +292,163 @@ private void packArray(StackItemForArray stackItem) throws IOException {
}
@Override
- public void writeFieldName(String name) throws IOException, JsonGenerationException {
+ public void writeFieldName(String name)
+ throws IOException, JsonGenerationException
+ {
addKeyToStackTop(name);
}
@Override
- public void writeString(String text) throws IOException, JsonGenerationException {
+ public void writeString(String text)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(text);
}
@Override
- public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+ public void writeString(char[] text, int offset, int len)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(new String(text, offset, len));
}
@Override
- public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException, JsonGenerationException {
+ public void writeRawUTF8String(byte[] text, int offset, int length)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(new String(text, offset, length, DEFAULT_CHARSET));
}
@Override
- public void writeUTF8String(byte[] text, int offset, int length) throws IOException, JsonGenerationException {
+ public void writeUTF8String(byte[] text, int offset, int length)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(new String(text, offset, length, DEFAULT_CHARSET));
}
@Override
- public void writeRaw(String text) throws IOException, JsonGenerationException {
+ public void writeRaw(String text)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(text);
}
@Override
- public void writeRaw(String text, int offset, int len) throws IOException, JsonGenerationException {
+ public void writeRaw(String text, int offset, int len)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(text.substring(0, len));
}
@Override
- public void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+ public void writeRaw(char[] text, int offset, int len)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(new String(text, offset, len));
}
@Override
- public void writeRaw(char c) throws IOException, JsonGenerationException {
+ public void writeRaw(char c)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(String.valueOf(c));
}
@Override
- public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException, JsonGenerationException {
+ public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(ByteBuffer.wrap(data, offset, len));
}
@Override
- public void writeNumber(int v) throws IOException, JsonGenerationException {
+ public void writeNumber(int v)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(Integer.valueOf(v));
}
@Override
- public void writeNumber(long v) throws IOException, JsonGenerationException {
+ public void writeNumber(long v)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(Long.valueOf(v));
}
@Override
- public void writeNumber(BigInteger v) throws IOException, JsonGenerationException {
+ public void writeNumber(BigInteger v)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(v);
}
@Override
- public void writeNumber(double d) throws IOException, JsonGenerationException {
+ public void writeNumber(double d)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(Double.valueOf(d));
}
@Override
- public void writeNumber(float f) throws IOException, JsonGenerationException {
+ public void writeNumber(float f)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(Float.valueOf(f));
}
@Override
- public void writeNumber(BigDecimal dec) throws IOException, JsonGenerationException {
+ public void writeNumber(BigDecimal dec)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(dec);
}
@Override
- public void writeNumber(String encodedValue) throws IOException, JsonGenerationException, UnsupportedOperationException {
+ public void writeNumber(String encodedValue)
+ throws IOException, JsonGenerationException, UnsupportedOperationException
+ {
throw new UnsupportedOperationException("writeNumber(String encodedValue) isn't supported yet");
}
@Override
- public void writeBoolean(boolean state) throws IOException, JsonGenerationException {
+ public void writeBoolean(boolean state)
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(Boolean.valueOf(state));
}
@Override
- public void writeNull() throws IOException, JsonGenerationException {
+ public void writeNull()
+ throws IOException, JsonGenerationException
+ {
addValueToStackTop(null);
}
+ public void writeExtensionType(MessagePackExtensionType extensionType)
+ throws IOException
+ {
+ addValueToStackTop(extensionType);
+ }
+
@Override
- public void close() throws IOException {
+ public void close()
+ throws IOException
+ {
try {
flush();
}
- catch (Exception e) {
- e.printStackTrace();
- }
finally {
- MessagePacker messagePacker = getMessagePacker();
- messagePacker.close();
+ if (isEnabled(Feature.AUTO_CLOSE_TARGET)) {
+ MessagePacker messagePacker = getMessagePacker();
+ messagePacker.close();
+ }
}
}
@Override
- public void flush() throws IOException {
+ public void flush()
+ throws IOException
+ {
if (rootStackItem != null) {
if (rootStackItem instanceof StackItemForObject) {
packObject((StackItemForObject) rootStackItem);
@@ -327,32 +459,43 @@ else if (rootStackItem instanceof StackItemForArray) {
else {
throw new IllegalStateException("Unexpected rootStackItem: " + rootStackItem);
}
- MessagePacker messagePacker = getMessagePacker();
- messagePacker.flush();
+ rootStackItem = null;
+ flushMessagePacker();
}
}
- @Override
- protected void _releaseBuffers() {
+ private void flushMessagePacker()
+ throws IOException
+ {
+ MessagePacker messagePacker = getMessagePacker();
+ messagePacker.flush();
+ }
+ @Override
+ protected void _releaseBuffers()
+ {
}
@Override
- protected void _verifyValueWrite(String typeMsg) throws IOException, JsonGenerationException {
+ protected void _verifyValueWrite(String typeMsg)
+ throws IOException, JsonGenerationException
+ {
int status = _writeContext.writeValue();
if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
- _reportError("Can not "+typeMsg+", expecting field name");
+ _reportError("Can not " + typeMsg + ", expecting field name");
}
}
- private StackItem getStackTop() {
+ private StackItem getStackTop()
+ {
if (stack.isEmpty()) {
throw new IllegalStateException("The stack is empty");
}
return stack.getFirst();
}
- private StackItemForObject getStackTopForObject() {
+ private StackItemForObject getStackTopForObject()
+ {
StackItem stackTop = getStackTop();
if (!(stackTop instanceof StackItemForObject)) {
throw new IllegalStateException("The stack top should be Object: " + stackTop);
@@ -360,7 +503,8 @@ private StackItemForObject getStackTopForObject() {
return (StackItemForObject) stackTop;
}
- private StackItemForArray getStackTopForArray() {
+ private StackItemForArray getStackTopForArray()
+ {
StackItem stackTop = getStackTop();
if (!(stackTop instanceof StackItemForArray)) {
throw new IllegalStateException("The stack top should be Array: " + stackTop);
@@ -368,15 +512,26 @@ private StackItemForArray getStackTopForArray() {
return (StackItemForArray) stackTop;
}
- private void addKeyToStackTop(String key) {
+ private void addKeyToStackTop(String key)
+ {
getStackTop().addKey(key);
}
- private void addValueToStackTop(Object value) {
- getStackTop().addValue(value);
+ private void addValueToStackTop(Object value)
+ throws IOException
+ {
+ if (stack.isEmpty()) {
+ packValue(value);
+ flushMessagePacker();
+ }
+ else {
+ getStackTop().addValue(value);
+ }
}
- private void popStackAndStoreTheItemAsValue() {
+ private void popStackAndStoreTheItemAsValue()
+ throws IOException
+ {
StackItem child = stack.pop();
if (stack.size() > 0) {
addValueToStackTop(child);
@@ -391,7 +546,8 @@ private void popStackAndStoreTheItemAsValue() {
}
}
- private MessagePacker getMessagePacker() {
+ private MessagePacker getMessagePacker()
+ {
MessagePacker messagePacker = messagePackersHolder.get();
if (messagePacker == null) {
throw new IllegalStateException("messagePacker is null");
diff --git a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackParser.java b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackParser.java
index 07e5c724f..e85ff7cd6 100644
--- a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackParser.java
+++ b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/MessagePackParser.java
@@ -1,18 +1,44 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.jackson.dataformat;
-import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.Base64Variant;
+import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonStreamContext;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.base.ParserMinimalBase;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.json.DupDetector;
import com.fasterxml.jackson.core.json.JsonReadContext;
-import org.msgpack.core.MessageFormat;
+import org.msgpack.core.MessagePack;
import org.msgpack.core.MessageUnpacker;
import org.msgpack.core.buffer.ArrayBufferInput;
import org.msgpack.core.buffer.InputStreamBufferInput;
import org.msgpack.core.buffer.MessageBufferInput;
-import org.msgpack.value.NumberValue;
+import org.msgpack.value.ExtensionValue;
+import org.msgpack.value.IntegerValue;
+import org.msgpack.value.Value;
+import org.msgpack.value.ValueFactory;
import org.msgpack.value.ValueType;
-import org.msgpack.value.holder.ValueHolder;
+import org.msgpack.value.Variable;
import java.io.IOException;
import java.io.InputStream;
@@ -20,85 +46,127 @@
import java.math.BigInteger;
import java.util.LinkedList;
-public class MessagePackParser extends ParserMinimalBase {
- private static final ThreadLocal messageUnpackerHolder = new ThreadLocal();
+public class MessagePackParser
+ extends ParserMinimalBase
+{
+ private static final ThreadLocal> messageUnpackerHolder =
+ new ThreadLocal>();
private ObjectCodec codec;
private JsonReadContext parsingContext;
private final LinkedList stack = new LinkedList();
- private final ValueHolder valueHolder = new ValueHolder();
+ private Value value = ValueFactory.newNil();
+ private Variable var = new Variable();
private boolean isClosed;
+ private long tokenPosition;
+ private long currentPosition;
+ private final IOContext ioContext;
- private static abstract class StackItem {
+ private abstract static class StackItem
+ {
private long numOfElements;
- protected StackItem(long numOfElements) {
+ protected StackItem(long numOfElements)
+ {
this.numOfElements = numOfElements;
}
- public void consume() {
- numOfElements--;
+ public void consume()
+ {
+ numOfElements--;
}
- public boolean isEmpty() {
+ public boolean isEmpty()
+ {
return numOfElements == 0;
}
}
- private static class StackItemForObject extends StackItem {
- StackItemForObject(long numOfElements) {
+ private static class StackItemForObject
+ extends StackItem
+ {
+ StackItemForObject(long numOfElements)
+ {
super(numOfElements);
}
}
- private static class StackItemForArray extends StackItem {
- StackItemForArray(long numOfElements) {
+ private static class StackItemForArray
+ extends StackItem
+ {
+ StackItemForArray(long numOfElements)
+ {
super(numOfElements);
}
}
- public MessagePackParser(IOContext ctxt, int features, InputStream in) throws IOException {
- this(ctxt, features, new InputStreamBufferInput(in));
+ public MessagePackParser(IOContext ctxt, int features, ObjectCodec objectCodec, InputStream in)
+ throws IOException
+ {
+ this(ctxt, features, new InputStreamBufferInput(in), objectCodec, in);
}
- public MessagePackParser(IOContext ctxt, int features, byte[] bytes) throws IOException {
- this(ctxt, features, new ArrayBufferInput(bytes));
+ public MessagePackParser(IOContext ctxt, int features, ObjectCodec objectCodec, byte[] bytes)
+ throws IOException
+ {
+ this(ctxt, features, new ArrayBufferInput(bytes), objectCodec, bytes);
}
- private MessagePackParser(IOContext ctxt, int features, MessageBufferInput input) throws IOException {
+ private MessagePackParser(IOContext ctxt, int features, MessageBufferInput input, ObjectCodec objectCodec, Object src)
+ throws IOException
+ {
+ super(features);
+
+ this.codec = objectCodec;
+ ioContext = ctxt;
DupDetector dups = Feature.STRICT_DUPLICATE_DETECTION.enabledIn(features)
? DupDetector.rootDetector(this) : null;
parsingContext = JsonReadContext.createRootContext(dups);
- MessageUnpacker messageUnpacker = messageUnpackerHolder.get();
- if (messageUnpacker == null) {
- messageUnpacker = new MessageUnpacker(input);
+ MessageUnpacker messageUnpacker;
+ Tuple messageUnpackerTuple = messageUnpackerHolder.get();
+ if (messageUnpackerTuple == null) {
+ messageUnpacker = MessagePack.newDefaultUnpacker(input);
}
else {
- messageUnpacker.reset(input);
+ // Considering to reuse InputStream with JsonParser.Feature.AUTO_CLOSE_SOURCE,
+ // MessagePackParser needs to use the MessageUnpacker that has the same InputStream
+ // since it has buffer which has loaded the InputStream data ahead.
+ // However, it needs to call MessageUnpacker#reset when the source is different from the previous one.
+ if (isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE) || messageUnpackerTuple.first() != src) {
+ messageUnpackerTuple.second().reset(input);
+ }
+ messageUnpacker = messageUnpackerTuple.second();
}
- messageUnpackerHolder.set(messageUnpacker);
+ messageUnpackerHolder.set(new Tuple(src, messageUnpacker));
}
@Override
- public ObjectCodec getCodec() {
+ public ObjectCodec getCodec()
+ {
return codec;
}
@Override
- public void setCodec(ObjectCodec c) {
+ public void setCodec(ObjectCodec c)
+ {
codec = c;
}
@Override
- public Version version() {
+ public Version version()
+ {
return null;
}
@Override
- public JsonToken nextToken() throws IOException, JsonParseException {
+ public JsonToken nextToken()
+ throws IOException, JsonParseException
+ {
MessageUnpacker messageUnpacker = getMessageUnpacker();
+ tokenPosition = messageUnpacker.getTotalReadBytes();
+
JsonToken nextToken = null;
if (parsingContext.inObject() || parsingContext.inArray()) {
if (stack.getFirst().isEmpty()) {
@@ -110,33 +178,56 @@ public JsonToken nextToken() throws IOException, JsonParseException {
}
}
- MessageFormat nextFormat = messageUnpacker.getNextFormat();
- ValueType valueType = nextFormat.getValueType();
+ if (!messageUnpacker.hasNext()) {
+ return null;
+ }
+
+ ValueType type = messageUnpacker.getNextFormat().getValueType();
// We should push a new StackItem lazily after updating the current stack.
StackItem newStack = null;
- switch (valueType) {
+ switch (type) {
case NIL:
messageUnpacker.unpackNil();
+ value = ValueFactory.newNil();
nextToken = JsonToken.VALUE_NULL;
break;
case BOOLEAN:
boolean b = messageUnpacker.unpackBoolean();
- nextToken = b ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE;
+ value = ValueFactory.newNil();
+ if (parsingContext.inObject() && _currToken != JsonToken.FIELD_NAME) {
+ parsingContext.setCurrentName(Boolean.toString(b));
+ nextToken = JsonToken.FIELD_NAME;
+ }
+ else {
+ nextToken = b ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE;
+ }
break;
case INTEGER:
- messageUnpacker.unpackValue(valueHolder);
- nextToken = JsonToken.VALUE_NUMBER_INT;
+ value = messageUnpacker.unpackValue(var);
+ if (parsingContext.inObject() && _currToken != JsonToken.FIELD_NAME) {
+ parsingContext.setCurrentName(value.asIntegerValue().toString());
+ nextToken = JsonToken.FIELD_NAME;
+ }
+ else {
+ nextToken = JsonToken.VALUE_NUMBER_INT;
+ }
break;
case FLOAT:
- messageUnpacker.unpackValue(valueHolder);
- nextToken = JsonToken.VALUE_NUMBER_FLOAT;
+ value = messageUnpacker.unpackValue(var);
+ if (parsingContext.inObject() && _currToken != JsonToken.FIELD_NAME) {
+ parsingContext.setCurrentName(value.asFloatValue().toString());
+ nextToken = JsonToken.FIELD_NAME;
+ }
+ else {
+ nextToken = JsonToken.VALUE_NUMBER_FLOAT;
+ }
break;
case STRING:
- messageUnpacker.unpackValue(valueHolder);
+ value = messageUnpacker.unpackValue(var);
if (parsingContext.inObject() && _currToken != JsonToken.FIELD_NAME) {
- parsingContext.setCurrentName(valueHolder.getRef().asRaw().toString());
+ parsingContext.setCurrentName(value.asRawValue().toString());
nextToken = JsonToken.FIELD_NAME;
}
else {
@@ -144,20 +235,31 @@ public JsonToken nextToken() throws IOException, JsonParseException {
}
break;
case BINARY:
- messageUnpacker.unpackValue(valueHolder);
- nextToken = JsonToken.VALUE_EMBEDDED_OBJECT;
+ value = messageUnpacker.unpackValue(var);
+ if (parsingContext.inObject() && _currToken != JsonToken.FIELD_NAME) {
+ parsingContext.setCurrentName(value.asRawValue().toString());
+ nextToken = JsonToken.FIELD_NAME;
+ }
+ else {
+ nextToken = JsonToken.VALUE_EMBEDDED_OBJECT;
+ }
break;
case ARRAY:
+ value = ValueFactory.newNil();
newStack = new StackItemForArray(messageUnpacker.unpackArrayHeader());
break;
case MAP:
+ value = ValueFactory.newNil();
newStack = new StackItemForObject(messageUnpacker.unpackMapHeader());
break;
- case EXTENDED:
- throw new UnsupportedOperationException();
+ case EXTENSION:
+ value = messageUnpacker.unpackValue(var);
+ nextToken = JsonToken.VALUE_EMBEDDED_OBJECT;
+ break;
default:
throw new IllegalStateException("Shouldn't reach here");
}
+ currentPosition = messageUnpacker.getTotalReadBytes();
if (parsingContext.inObject() && nextToken != JsonToken.FIELD_NAME || parsingContext.inArray()) {
stack.getFirst().consume();
@@ -180,110 +282,183 @@ else if (newStack instanceof StackItemForObject) {
}
@Override
- protected void _handleEOF() throws JsonParseException {}
+ protected void _handleEOF()
+ throws JsonParseException
+ {
+ }
@Override
- public String getText() throws IOException, JsonParseException {
+ public String getText()
+ throws IOException, JsonParseException
+ {
// This method can be called for new BigInteger(text)
- return valueHolder.getRef().toString();
+ if (value.isRawValue()) {
+ return value.asRawValue().toString();
+ }
+ else {
+ return value.toString();
+ }
}
@Override
- public char[] getTextCharacters() throws IOException, JsonParseException {
+ public char[] getTextCharacters()
+ throws IOException, JsonParseException
+ {
return getText().toCharArray();
}
@Override
- public boolean hasTextCharacters() {
+ public boolean hasTextCharacters()
+ {
return false;
}
@Override
- public int getTextLength() throws IOException, JsonParseException {
+ public int getTextLength()
+ throws IOException, JsonParseException
+ {
return getText().length();
}
@Override
- public int getTextOffset() throws IOException, JsonParseException {
+ public int getTextOffset()
+ throws IOException, JsonParseException
+ {
return 0;
}
@Override
- public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException {
- return valueHolder.getRef().asBinary().toByteArray();
+ public byte[] getBinaryValue(Base64Variant b64variant)
+ throws IOException, JsonParseException
+ {
+ return value.asRawValue().asByteArray();
}
@Override
- public Number getNumberValue() throws IOException, JsonParseException {
- NumberValue numberValue = valueHolder.getRef().asNumber();
- if (numberValue.isValidInt()) {
- return numberValue.toInt();
- }
- else if (numberValue.isValidLong()) {
- return numberValue.toLong();
+ public Number getNumberValue()
+ throws IOException, JsonParseException
+ {
+ if (value.isIntegerValue()) {
+ IntegerValue integerValue = value.asIntegerValue();
+ if (integerValue.isInIntRange()) {
+ return integerValue.toInt();
+ }
+ else if (integerValue.isInLongRange()) {
+ return integerValue.toLong();
+ }
+ else {
+ return integerValue.toBigInteger();
+ }
}
else {
- return numberValue.toBigInteger();
+ return value.asNumberValue().toDouble();
}
}
@Override
- public int getIntValue() throws IOException, JsonParseException {
- return valueHolder.getRef().asNumber().toInt();
+ public int getIntValue()
+ throws IOException, JsonParseException
+ {
+ return value.asNumberValue().toInt();
}
@Override
- public long getLongValue() throws IOException, JsonParseException {
- return valueHolder.getRef().asNumber().toLong();
+ public long getLongValue()
+ throws IOException, JsonParseException
+ {
+ return value.asNumberValue().toLong();
}
@Override
- public BigInteger getBigIntegerValue() throws IOException, JsonParseException {
- return valueHolder.getRef().asNumber().toBigInteger();
+ public BigInteger getBigIntegerValue()
+ throws IOException, JsonParseException
+ {
+ return value.asNumberValue().toBigInteger();
}
@Override
- public float getFloatValue() throws IOException, JsonParseException {
- return valueHolder.getRef().asFloat().toFloat();
+ public float getFloatValue()
+ throws IOException, JsonParseException
+ {
+ return value.asNumberValue().toFloat();
}
@Override
- public double getDoubleValue() throws IOException, JsonParseException {
- return valueHolder.getRef().asFloat().toDouble();
+ public double getDoubleValue()
+ throws IOException, JsonParseException
+ {
+ return value.asNumberValue().toDouble();
}
@Override
- public BigDecimal getDecimalValue() throws IOException {
- return null;
+ public BigDecimal getDecimalValue()
+ throws IOException
+ {
+ if (value.isIntegerValue()) {
+ IntegerValue number = value.asIntegerValue();
+ //optimization to not convert the value to BigInteger unnecessarily
+ if (number.isInLongRange()) {
+ return BigDecimal.valueOf(number.toLong());
+ }
+ else {
+ return new BigDecimal(number.toBigInteger());
+ }
+ }
+ else if (value.isFloatValue()) {
+ return BigDecimal.valueOf(value.asFloatValue().toDouble());
+ }
+ else {
+ throw new UnsupportedOperationException("Couldn't parse value as BigDecimal. " + value);
+ }
}
@Override
- public Object getEmbeddedObject() throws IOException, JsonParseException {
- return valueHolder.getRef().asBinary().toByteArray();
+ public Object getEmbeddedObject()
+ throws IOException, JsonParseException
+ {
+ if (value.isBinaryValue()) {
+ return value.asBinaryValue().asByteArray();
+ }
+ else if (value.isExtensionValue()) {
+ ExtensionValue extensionValue = value.asExtensionValue();
+ return new MessagePackExtensionType(extensionValue.getType(), extensionValue.getData());
+ }
+ else {
+ throw new UnsupportedOperationException();
+ }
}
@Override
- public NumberType getNumberType() throws IOException, JsonParseException {
- NumberValue numberValue = valueHolder.getRef().asNumber();
- if (numberValue.isValidInt()) {
- return NumberType.INT;
- }
- else if (numberValue.isValidLong()) {
- return NumberType.LONG;
+ public NumberType getNumberType()
+ throws IOException, JsonParseException
+ {
+ if (value.isIntegerValue()) {
+ IntegerValue integerValue = value.asIntegerValue();
+ if (integerValue.isInIntRange()) {
+ return NumberType.INT;
+ }
+ else if (integerValue.isInLongRange()) {
+ return NumberType.LONG;
+ }
+ else {
+ return NumberType.BIG_INTEGER;
+ }
}
else {
- return NumberType.BIG_INTEGER;
+ value.asNumberValue();
+ return NumberType.DOUBLE;
}
}
@Override
- public void close() throws IOException {
+ public void close()
+ throws IOException
+ {
try {
- MessageUnpacker messageUnpacker = getMessageUnpacker();
- messageUnpacker.close();
- }
- catch (Exception e) {
- e.printStackTrace();
+ if (isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)) {
+ MessageUnpacker messageUnpacker = getMessageUnpacker();
+ messageUnpacker.close();
+ }
}
finally {
isClosed = true;
@@ -291,31 +466,50 @@ public void close() throws IOException {
}
@Override
- public boolean isClosed() {
+ public boolean isClosed()
+ {
return isClosed;
}
@Override
- public JsonStreamContext getParsingContext() {
+ public JsonStreamContext getParsingContext()
+ {
return parsingContext;
}
@Override
- public JsonLocation getTokenLocation() {
- throw new UnsupportedOperationException("Not implemented yet");
+ public JsonLocation getTokenLocation()
+ {
+ return new JsonLocation(ioContext.getSourceReference(), tokenPosition, -1, -1, (int) tokenPosition);
}
@Override
- public JsonLocation getCurrentLocation() {
- throw new UnsupportedOperationException("Not implemented yet");
+ public JsonLocation getCurrentLocation()
+ {
+ return new JsonLocation(ioContext.getSourceReference(), currentPosition, -1, -1, (int) currentPosition);
}
@Override
- public void overrideCurrentName(String name) {
- throw new UnsupportedOperationException("Not implemented yet");
+ public void overrideCurrentName(String name)
+ {
+ try {
+ if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
+ JsonReadContext parent = parsingContext.getParent();
+ parent.setCurrentName(name);
+ }
+ else {
+ parsingContext.setCurrentName(name);
+ }
+ }
+ catch (JsonProcessingException e) {
+ throw new IllegalStateException(e);
+ }
}
- @Override public String getCurrentName() throws IOException {
+ @Override
+ public String getCurrentName()
+ throws IOException
+ {
if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
JsonReadContext parent = parsingContext.getParent();
return parent.getCurrentName();
@@ -323,11 +517,12 @@ public void overrideCurrentName(String name) {
return parsingContext.getCurrentName();
}
- private MessageUnpacker getMessageUnpacker() {
- MessageUnpacker messageUnpacker = messageUnpackerHolder.get();
- if (messageUnpacker == null) {
+ private MessageUnpacker getMessageUnpacker()
+ {
+ Tuple messageUnpackerTuple = messageUnpackerHolder.get();
+ if (messageUnpackerTuple == null) {
throw new IllegalStateException("messageUnpacker is null");
}
- return messageUnpacker;
+ return messageUnpackerTuple.second();
}
}
diff --git a/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/Tuple.java b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/Tuple.java
new file mode 100644
index 000000000..1a252739f
--- /dev/null
+++ b/msgpack-jackson/src/main/java/org/msgpack/jackson/dataformat/Tuple.java
@@ -0,0 +1,41 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.jackson.dataformat;
+
+/**
+ * Created by komamitsu on 5/28/15.
+ */
+public class Tuple
+{
+ private final F first;
+ private final S second;
+
+ public Tuple(F first, S second)
+ {
+ this.first = first;
+ this.second = second;
+ }
+
+ public F first()
+ {
+ return first;
+ }
+
+ public S second()
+ {
+ return second;
+ }
+}
diff --git a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/ExampleOfTypeInformationSerDe.java b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/ExampleOfTypeInformationSerDe.java
new file mode 100644
index 000000000..5414b0bdc
--- /dev/null
+++ b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/ExampleOfTypeInformationSerDe.java
@@ -0,0 +1,167 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.jackson.dataformat;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.TreeNode;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class ExampleOfTypeInformationSerDe
+ extends MessagePackDataformatTestBase
+{
+ static class A
+ {
+ private List list = new ArrayList();
+
+ public List getList()
+ {
+ return list;
+ }
+
+ public void setList(List list)
+ {
+ this.list = list;
+ }
+ }
+
+ static class B
+ {
+ private String str;
+
+ public String getStr()
+ {
+ return str;
+ }
+
+ public void setStr(String str)
+ {
+ this.str = str;
+ }
+ }
+
+ @JsonSerialize(using = ObjectContainerSerializer.class)
+ @JsonDeserialize(using = ObjectContainerDeserializer.class)
+ static class ObjectContainer
+ {
+ private final Map objects;
+
+ public ObjectContainer(Map objects)
+ {
+ this.objects = objects;
+ }
+
+ public Map getObjects()
+ {
+ return objects;
+ }
+ }
+
+ static class ObjectContainerSerializer
+ extends JsonSerializer
+ {
+ @Override
+ public void serialize(ObjectContainer value, JsonGenerator gen, SerializerProvider serializers)
+ throws IOException, JsonProcessingException
+ {
+ gen.writeStartObject();
+ HashMap metadata = new HashMap();
+ for (Map.Entry entry : value.getObjects().entrySet()) {
+ metadata.put(entry.getKey(), entry.getValue().getClass().getName());
+ }
+ gen.writeObjectField("__metadata", metadata);
+ gen.writeObjectField("objects", value.getObjects());
+ gen.writeEndObject();
+ }
+ }
+
+ static class ObjectContainerDeserializer
+ extends JsonDeserializer
+ {
+ @Override
+ public ObjectContainer deserialize(JsonParser p, DeserializationContext ctxt)
+ throws IOException, JsonProcessingException
+ {
+ ObjectContainer objectContainer = new ObjectContainer(new HashMap());
+ TreeNode treeNode = p.readValueAsTree();
+
+ Map metadata = treeNode.get("__metadata").traverse(p.getCodec()).readValueAs(new TypeReference>() {});
+ TreeNode dataMapTree = treeNode.get("objects");
+ for (Map.Entry entry : metadata.entrySet()) {
+ try {
+ Object o = dataMapTree.get(entry.getKey()).traverse(p.getCodec()).readValueAs(Class.forName(entry.getValue()));
+ objectContainer.getObjects().put(entry.getKey(), o);
+ }
+ catch (ClassNotFoundException e) {
+ throw new RuntimeException("Failed to deserialize: " + entry, e);
+ }
+ }
+
+ return objectContainer;
+ }
+ }
+
+ @Test
+ public void test()
+ throws IOException
+ {
+ ObjectContainer objectContainer = new ObjectContainer(new HashMap());
+ {
+ A a = new A();
+ a.setList(Arrays.asList("first", "second", "third"));
+ objectContainer.getObjects().put("a", a);
+
+ B b = new B();
+ b.setStr("hello world");
+ objectContainer.getObjects().put("b", b);
+
+ Double pi = 3.14;
+ objectContainer.getObjects().put("pi", pi);
+ }
+
+ ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ byte[] bytes = objectMapper.writeValueAsBytes(objectContainer);
+ ObjectContainer restored = objectMapper.readValue(bytes, ObjectContainer.class);
+
+ {
+ assertEquals(3, restored.getObjects().size());
+ A a = (A) restored.getObjects().get("a");
+ assertArrayEquals(new String[] {"first", "second", "third"}, a.getList().toArray());
+ B b = (B) restored.getObjects().get("b");
+ assertEquals("hello world", b.getStr());
+ assertEquals(3.14, restored.getObjects().get("pi"));
+ }
+ }
+}
diff --git a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatForPojoTest.java b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatForPojoTest.java
index a27bc27fa..5c1559770 100644
--- a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatForPojoTest.java
+++ b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatForPojoTest.java
@@ -1,3 +1,18 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.jackson.dataformat;
import org.junit.Test;
@@ -8,9 +23,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-public class MessagePackDataformatForPojoTest extends MessagePackDataformatTestBase {
+public class MessagePackDataformatForPojoTest
+ extends MessagePackDataformatTestBase
+{
@Test
- public void testNormal() throws IOException {
+ public void testNormal()
+ throws IOException
+ {
byte[] bytes = objectMapper.writeValueAsBytes(normalPojo);
NormalPojo value = objectMapper.readValue(bytes, NormalPojo.class);
assertEquals(normalPojo.s, value.getS());
@@ -25,7 +44,9 @@ public void testNormal() throws IOException {
}
@Test
- public void testNestedList() throws IOException {
+ public void testNestedList()
+ throws IOException
+ {
byte[] bytes = objectMapper.writeValueAsBytes(nestedListPojo);
NestedListPojo value = objectMapper.readValue(bytes, NestedListPojo.class);
assertEquals(nestedListPojo.s, value.s);
@@ -33,7 +54,9 @@ public void testNestedList() throws IOException {
}
@Test
- public void testNestedListComplex() throws IOException {
+ public void testNestedListComplex()
+ throws IOException
+ {
byte[] bytes = objectMapper.writeValueAsBytes(nestedListComplexPojo);
NestedListComplexPojo value = objectMapper.readValue(bytes, NestedListComplexPojo.class);
assertEquals(nestedListPojo.s, value.s);
@@ -41,7 +64,9 @@ public void testNestedListComplex() throws IOException {
}
@Test
- public void testUsingCustomConstructor() throws IOException {
+ public void testUsingCustomConstructor()
+ throws IOException
+ {
UsingCustomConstructorPojo orig = new UsingCustomConstructorPojo("komamitsu", 55);
byte[] bytes = objectMapper.writeValueAsBytes(orig);
UsingCustomConstructorPojo value = objectMapper.readValue(bytes, UsingCustomConstructorPojo.class);
@@ -50,7 +75,9 @@ public void testUsingCustomConstructor() throws IOException {
}
@Test
- public void testIgnoringProperties() throws IOException {
+ public void testIgnoringProperties()
+ throws IOException
+ {
IgnoringPropertiesPojo orig = new IgnoringPropertiesPojo();
orig.internal = "internal";
orig.external = "external";
@@ -63,12 +90,13 @@ public void testIgnoringProperties() throws IOException {
}
@Test
- public void testChangingPropertyNames() throws IOException {
+ public void testChangingPropertyNames()
+ throws IOException
+ {
ChangingPropertyNamesPojo orig = new ChangingPropertyNamesPojo();
orig.setTheName("komamitsu");
byte[] bytes = objectMapper.writeValueAsBytes(orig);
ChangingPropertyNamesPojo value = objectMapper.readValue(bytes, ChangingPropertyNamesPojo.class);
assertEquals("komamitsu", value.getTheName());
}
-
}
diff --git a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatTestBase.java b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatTestBase.java
index ba315d7fc..1d5156adc 100644
--- a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatTestBase.java
+++ b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackDataformatTestBase.java
@@ -1,24 +1,40 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.jackson.dataformat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.commons.math3.stat.StatUtils;
-import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;
import org.junit.After;
import org.junit.Before;
-import org.msgpack.jackson.dataformat.MessagePackFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-public class MessagePackDataformatTestBase {
+public class MessagePackDataformatTestBase
+{
protected MessagePackFactory factory;
protected ByteArrayOutputStream out;
protected ByteArrayInputStream in;
@@ -29,7 +45,8 @@ public class MessagePackDataformatTestBase {
protected TinyPojo tinyPojo;
@Before
- public void setup() {
+ public void setup()
+ {
factory = new MessagePackFactory();
objectMapper = new ObjectMapper(factory);
out = new ByteArrayOutputStream();
@@ -59,11 +76,13 @@ public void setup() {
}
@After
- public void teardown() {
+ public void teardown()
+ {
if (in != null) {
try {
in.close();
- } catch (IOException e) {
+ }
+ catch (IOException e) {
e.printStackTrace();
}
}
@@ -71,41 +90,37 @@ public void teardown() {
if (out != null) {
try {
out.close();
- } catch (IOException e) {
+ }
+ catch (IOException e) {
e.printStackTrace();
}
}
}
- protected void printStat(String label, double[] values) {
- StandardDeviation standardDeviation = new StandardDeviation();
- System.out.println(label + ":");
- System.out.println(String.format(" mean : %.2f", StatUtils.mean(values)));
- System.out.println(String.format(" min : %.2f", StatUtils.min(values)));
- System.out.println(String.format(" max : %.2f", StatUtils.max(values)));
- System.out.println(String.format(" stdev: %.2f", standardDeviation.evaluate(values)));
- System.out.println("");
- }
-
- public enum Suit {
+ public enum Suit
+ {
SPADE, HEART, DIAMOND, CLUB;
}
- public static class NestedListPojo {
+ public static class NestedListPojo
+ {
public String s;
public List strs;
}
- public static class TinyPojo {
+ public static class TinyPojo
+ {
public String t;
}
- public static class NestedListComplexPojo {
+ public static class NestedListComplexPojo
+ {
public String s;
public List foos;
}
- public static class NormalPojo {
+ public static class NormalPojo
+ {
String s;
public boolean bool;
public int i;
@@ -116,11 +131,13 @@ public static class NormalPojo {
public BigInteger bi;
public Suit suit;
- public String getS() {
+ public String getS()
+ {
return s;
}
- public void setS(String s) {
+ public void setS(String s)
+ {
this.s = s;
}
}
@@ -136,19 +153,21 @@ public UsingCustomConstructorPojo(@JsonProperty("name") String name, @JsonProper
this.age = age;
}
- public String getName() {
+ public String getName()
+ {
return name;
}
- public int getAge() {
+ public int getAge()
+ {
return age;
}
}
- @JsonIgnoreProperties({ "foo", "bar" })
+ @JsonIgnoreProperties({"foo", "bar"})
public static class IgnoringPropertiesPojo
{
- int _code;
+ int code;
// will not be written as JSON; nor assigned from JSON:
@JsonIgnore
@@ -158,22 +177,65 @@ public static class IgnoringPropertiesPojo
public String external;
@JsonIgnore
- public void setCode(int c) { _code = c; }
+ public void setCode(int c)
+ {
+ code = c;
+ }
// note: will also be ignored because setter has annotation!
- public int getCode() { return _code; }
+ public int getCode()
+ {
+ return code;
+ }
}
- public static class ChangingPropertyNamesPojo {
- String _name;
+ public static class ChangingPropertyNamesPojo
+ {
+ String name;
// without annotation, we'd get "theName", but we want "name":
@JsonProperty("name")
- public String getTheName() { return _name; }
+ public String getTheName()
+ {
+ return name;
+ }
// note: it is enough to add annotation on just getter OR setter;
// so we can omit it here
- public void setTheName(String n) { _name = n; }
+ public void setTheName(String n)
+ {
+ name = n;
+ }
+ }
+
+ protected interface FileSetup
+ {
+ void setup(File f)
+ throws Exception;
}
+ protected File createTempFile()
+ throws Exception
+ {
+ return createTempFile(null);
+ }
+
+ protected File createTempFile(FileSetup fileSetup)
+ throws Exception
+ {
+ File tempFile = File.createTempFile("test", "msgpack");
+ tempFile.deleteOnExit();
+ if (fileSetup != null) {
+ fileSetup.setup(tempFile);
+ }
+ return tempFile;
+ }
+
+ protected OutputStream createTempFileOutputStream()
+ throws IOException
+ {
+ File tempFile = File.createTempFile("test", "msgpack");
+ tempFile.deleteOnExit();
+ return new FileOutputStream(tempFile);
+ }
}
diff --git a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackFactoryTest.java b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackFactoryTest.java
index 867229518..25180f784 100644
--- a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackFactoryTest.java
+++ b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackFactoryTest.java
@@ -1,3 +1,18 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.jackson.dataformat;
import com.fasterxml.jackson.core.JsonEncoding;
@@ -9,16 +24,22 @@
import static org.junit.Assert.assertEquals;
-public class MessagePackFactoryTest extends MessagePackDataformatTestBase {
+public class MessagePackFactoryTest
+ extends MessagePackDataformatTestBase
+{
@Test
- public void testCreateGenerator() throws IOException {
+ public void testCreateGenerator()
+ throws IOException
+ {
JsonEncoding enc = JsonEncoding.UTF8;
JsonGenerator generator = factory.createGenerator(out, enc);
assertEquals(MessagePackGenerator.class, generator.getClass());
}
@Test
- public void testCreateParser() throws IOException {
+ public void testCreateParser()
+ throws IOException
+ {
JsonParser parser = factory.createParser(in);
assertEquals(MessagePackParser.class, parser.getClass());
}
diff --git a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackGeneratorTest.java b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackGeneratorTest.java
index 9dba2ea1c..fd2ea313f 100644
--- a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackGeneratorTest.java
+++ b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackGeneratorTest.java
@@ -15,22 +15,46 @@
//
package org.msgpack.jackson.dataformat;
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
+import org.msgpack.core.ExtensionTypeHeader;
+import org.msgpack.core.MessagePack;
import org.msgpack.core.MessageUnpacker;
import org.msgpack.core.buffer.ArrayBufferInput;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-public class MessagePackGeneratorTest extends MessagePackDataformatTestBase {
+public class MessagePackGeneratorTest
+ extends MessagePackDataformatTestBase
+{
@Test
- public void testGeneratorShouldWriteObject() throws IOException {
+ public void testGeneratorShouldWriteObject()
+ throws IOException
+ {
Map hashMap = new HashMap();
// #1
hashMap.put("str", "komamitsu");
@@ -45,7 +69,7 @@ public void testGeneratorShouldWriteObject() throws IOException {
// #6
hashMap.put("double", 3.14159d);
// #7
- hashMap.put("bin", new byte[]{0x00, 0x01, (byte)0xFE, (byte)0xFF});
+ hashMap.put("bin", new byte[] {0x00, 0x01, (byte) 0xFE, (byte) 0xFF});
// #8
Map childObj = new HashMap();
childObj.put("co_str", "child#0");
@@ -56,10 +80,13 @@ public void testGeneratorShouldWriteObject() throws IOException {
childArray.add("child#1");
childArray.add(1.23f);
hashMap.put("childArray", childArray);
+ // #10
+ byte[] hello = "hello".getBytes("UTF-8");
+ hashMap.put("ext", new MessagePackExtensionType((byte) 17, hello));
long bitmap = 0;
byte[] bytes = objectMapper.writeValueAsBytes(hashMap);
- MessageUnpacker messageUnpacker = new MessageUnpacker(new ArrayBufferInput(bytes));
+ MessageUnpacker messageUnpacker = MessagePack.newDefaultUnpacker(new ArrayBufferInput(bytes));
assertEquals(hashMap.size(), messageUnpacker.unpackMapHeader());
for (int i = 0; i < hashMap.size(); i++) {
String key = messageUnpacker.unpackString();
@@ -95,11 +122,11 @@ else if (key.equals("double")) {
}
else if (key.equals("bin")) {
// #7
- assertEquals(4, messageUnpacker.unpackBinaryHeader());
- assertEquals((byte)0x00, messageUnpacker.unpackByte());
- assertEquals((byte)0x01, messageUnpacker.unpackByte());
- assertEquals((byte)0xFE, messageUnpacker.unpackByte());
- assertEquals((byte)0xFF, messageUnpacker.unpackByte());
+ assertEquals(4, messageUnpacker.unpackBinaryHeader());
+ assertEquals((byte) 0x00, messageUnpacker.unpackByte());
+ assertEquals((byte) 0x01, messageUnpacker.unpackByte());
+ assertEquals((byte) 0xFE, messageUnpacker.unpackByte());
+ assertEquals((byte) 0xFF, messageUnpacker.unpackByte());
bitmap |= 0x1 << 6;
}
else if (key.equals("childObj")) {
@@ -127,15 +154,30 @@ else if (key.equals("childArray")) {
assertEquals(1.23f, messageUnpacker.unpackFloat(), 0.01f);
bitmap |= 0x1 << 9;
}
+ else if (key.equals("ext")) {
+ // #10
+ ExtensionTypeHeader header = messageUnpacker.unpackExtensionTypeHeader();
+ assertEquals(17, header.getType());
+ assertEquals(5, header.getLength());
+ ByteBuffer payload = ByteBuffer.allocate(header.getLength());
+ payload.flip();
+ payload.limit(payload.capacity());
+ messageUnpacker.readPayload(payload);
+ payload.flip();
+ assertArrayEquals("hello".getBytes(), payload.array());
+ bitmap |= 0x1 << 10;
+ }
else {
assertTrue(false);
}
}
- assertEquals(0x03FF, bitmap);
+ assertEquals(0x07FF, bitmap);
}
@Test
- public void testGeneratorShouldWriteArray() throws IOException {
+ public void testGeneratorShouldWriteArray()
+ throws IOException
+ {
List array = new ArrayList();
// #1
array.add("komamitsu");
@@ -157,7 +199,7 @@ public void testGeneratorShouldWriteArray() throws IOException {
long bitmap = 0;
byte[] bytes = objectMapper.writeValueAsBytes(array);
- MessageUnpacker messageUnpacker = new MessageUnpacker(new ArrayBufferInput(bytes));
+ MessageUnpacker messageUnpacker = MessagePack.newDefaultUnpacker(new ArrayBufferInput(bytes));
assertEquals(array.size(), messageUnpacker.unpackArrayHeader());
// #1
assertEquals("komamitsu", messageUnpacker.unpackString());
@@ -189,4 +231,208 @@ else if (key.equals("num")) {
// #7
assertEquals(false, messageUnpacker.unpackBoolean());
}
+
+ @Test
+ public void testMessagePackGeneratorDirectly()
+ throws Exception
+ {
+ MessagePackFactory messagePackFactory = new MessagePackFactory();
+ File tempFile = createTempFile();
+
+ JsonGenerator generator = messagePackFactory.createGenerator(tempFile, JsonEncoding.UTF8);
+ assertTrue(generator instanceof MessagePackGenerator);
+ generator.writeStartArray();
+ generator.writeNumber(0);
+ generator.writeString("one");
+ generator.writeNumber(2.0f);
+ generator.writeEndArray();
+ generator.flush();
+ generator.flush(); // intentional
+ generator.close();
+
+ FileInputStream fileInputStream = new FileInputStream(tempFile);
+ MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(fileInputStream);
+ assertEquals(3, unpacker.unpackArrayHeader());
+ assertEquals(0, unpacker.unpackInt());
+ assertEquals("one", unpacker.unpackString());
+ assertEquals(2.0f, unpacker.unpackFloat(), 0.001f);
+ assertFalse(unpacker.hasNext());
+ }
+
+ @Test
+ public void testWritePrimitives()
+ throws Exception
+ {
+ MessagePackFactory messagePackFactory = new MessagePackFactory();
+ File tempFile = createTempFile();
+
+ JsonGenerator generator = messagePackFactory.createGenerator(tempFile, JsonEncoding.UTF8);
+ assertTrue(generator instanceof MessagePackGenerator);
+ generator.writeNumber(0);
+ generator.writeString("one");
+ generator.writeNumber(2.0f);
+ generator.flush();
+ generator.close();
+
+ FileInputStream fileInputStream = new FileInputStream(tempFile);
+ MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(fileInputStream);
+ assertEquals(0, unpacker.unpackInt());
+ assertEquals("one", unpacker.unpackString());
+ assertEquals(2.0f, unpacker.unpackFloat(), 0.001f);
+ assertFalse(unpacker.hasNext());
+ }
+
+ @Test
+ public void testBigDecimal()
+ throws IOException
+ {
+ ObjectMapper mapper = new ObjectMapper(new MessagePackFactory());
+
+ {
+ double d0 = 1.23456789;
+ double d1 = 1.23450000000000000000006789;
+ String d2 = "12.30";
+ List bigDecimals = Arrays.asList(
+ BigDecimal.valueOf(d0),
+ BigDecimal.valueOf(d1),
+ new BigDecimal(d2),
+ BigDecimal.valueOf(Double.MIN_VALUE),
+ BigDecimal.valueOf(Double.MAX_VALUE),
+ BigDecimal.valueOf(Double.MIN_NORMAL)
+ );
+
+ byte[] bytes = mapper.writeValueAsBytes(bigDecimals);
+ MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(bytes);
+
+ assertEquals(bigDecimals.size(), unpacker.unpackArrayHeader());
+ assertEquals(d0, unpacker.unpackDouble(), 0.000000000000001);
+ assertEquals(d1, unpacker.unpackDouble(), 0.000000000000001);
+ assertEquals(Double.valueOf(d2), unpacker.unpackDouble(), 0.000000000000001);
+ assertEquals(Double.MIN_VALUE, unpacker.unpackDouble(), 0.000000000000001);
+ assertEquals(Double.MAX_VALUE, unpacker.unpackDouble(), 0.000000000000001);
+ assertEquals(Double.MIN_NORMAL, unpacker.unpackDouble(), 0.000000000000001);
+ }
+
+ {
+ BigDecimal decimal = new BigDecimal("1234.567890123456789012345678901234567890");
+ List bigDecimals = Arrays.asList(
+ decimal
+ );
+
+ try {
+ mapper.writeValueAsBytes(bigDecimals);
+ assertTrue(false);
+ }
+ catch (IllegalArgumentException e) {
+ assertTrue(true);
+ }
+ }
+ }
+
+ @Test(expected = IOException.class)
+ public void testEnableFeatureAutoCloseTarget()
+ throws IOException
+ {
+ OutputStream out = createTempFileOutputStream();
+ MessagePackFactory messagePackFactory = new MessagePackFactory();
+ ObjectMapper objectMapper = new ObjectMapper(messagePackFactory);
+ List integers = Arrays.asList(1);
+ objectMapper.writeValue(out, integers);
+ objectMapper.writeValue(out, integers);
+ }
+
+ @Test
+ public void testDisableFeatureAutoCloseTarget()
+ throws Exception
+ {
+ File tempFile = createTempFile();
+ OutputStream out = new FileOutputStream(tempFile);
+ MessagePackFactory messagePackFactory = new MessagePackFactory();
+ ObjectMapper objectMapper = new ObjectMapper(messagePackFactory);
+ objectMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
+ List integers = Arrays.asList(1);
+ objectMapper.writeValue(out, integers);
+ objectMapper.writeValue(out, integers);
+ out.close();
+
+ MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(new FileInputStream(tempFile));
+ assertEquals(1, unpacker.unpackArrayHeader());
+ assertEquals(1, unpacker.unpackInt());
+ assertEquals(1, unpacker.unpackArrayHeader());
+ assertEquals(1, unpacker.unpackInt());
+ }
+
+ @Test
+ public void testWritePrimitiveObjectViaObjectMapper()
+ throws Exception
+ {
+ File tempFile = createTempFile();
+ OutputStream out = new FileOutputStream(tempFile);
+
+ ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ objectMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
+ objectMapper.writeValue(out, 1);
+ objectMapper.writeValue(out, "two");
+ objectMapper.writeValue(out, 3.14);
+ objectMapper.writeValue(out, Arrays.asList(4));
+ objectMapper.writeValue(out, 5L);
+
+ MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(new FileInputStream(tempFile));
+ assertEquals(1, unpacker.unpackInt());
+ assertEquals("two", unpacker.unpackString());
+ assertEquals(3.14, unpacker.unpackFloat(), 0.0001);
+ assertEquals(1, unpacker.unpackArrayHeader());
+ assertEquals(4, unpacker.unpackInt());
+ assertEquals(5, unpacker.unpackLong());
+ }
+
+ @Test
+ public void testInMultiThreads()
+ throws Exception
+ {
+ int threadCount = 8;
+ final int loopCount = 4000;
+ ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
+ final ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ objectMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
+ final List buffers = new ArrayList(threadCount);
+ List> results = new ArrayList>();
+
+ for (int ti = 0; ti < threadCount; ti++) {
+ buffers.add(new ByteArrayOutputStream());
+ final int threadIndex = ti;
+ results.add(executorService.submit(new Callable()
+ {
+ @Override
+ public Exception call()
+ throws Exception
+ {
+ try {
+ for (int i = 0; i < loopCount; i++) {
+ objectMapper.writeValue(buffers.get(threadIndex), threadIndex);
+ }
+ return null;
+ }
+ catch (IOException e) {
+ return e;
+ }
+ }
+ }));
+ }
+
+ for (int ti = 0; ti < threadCount; ti++) {
+ Future exceptionFuture = results.get(ti);
+ Exception exception = exceptionFuture.get(20, TimeUnit.SECONDS);
+ if (exception != null) {
+ throw exception;
+ }
+ else {
+ ByteArrayOutputStream outputStream = buffers.get(ti);
+ MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(outputStream.toByteArray());
+ for (int i = 0; i < loopCount; i++) {
+ assertEquals(ti, unpacker.unpackInt());
+ }
+ }
+ }
+ }
}
diff --git a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackParserTest.java b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackParserTest.java
index 0b8cae651..112390598 100644
--- a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackParserTest.java
+++ b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/MessagePackParserTest.java
@@ -1,24 +1,60 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.jackson.dataformat;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
import org.junit.Test;
+import org.msgpack.core.MessagePack;
import org.msgpack.core.MessagePacker;
-import org.msgpack.core.buffer.OutputStreamBufferOutput;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
+import java.math.BigDecimal;
import java.math.BigInteger;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
-public class MessagePackParserTest extends MessagePackDataformatTestBase {
+public class MessagePackParserTest
+ extends MessagePackDataformatTestBase
+{
@Test
- public void testParserShouldReadObject() throws IOException {
- MessagePacker packer = new MessagePacker(new OutputStreamBufferOutput(out));
- packer.packMapHeader(8);
+ public void testParserShouldReadObject()
+ throws IOException
+ {
+ MessagePacker packer = MessagePack.newDefaultPacker(out);
+ packer.packMapHeader(9);
// #1
packer.packString("str");
packer.packString("foobar");
@@ -55,14 +91,19 @@ public void testParserShouldReadObject() throws IOException {
// #8
packer.packString("bool");
packer.packBoolean(false);
+ // #9
+ byte[] extPayload = {-80, -50, -25, -114, -25, 16, 60, 68};
+ packer.packString("ext");
+ packer.packExtensionTypeHeader((byte) 0, extPayload.length);
+ packer.writePayload(extPayload);
packer.flush();
byte[] bytes = out.toByteArray();
- TypeReference> typeReference = new TypeReference>(){};
+ TypeReference> typeReference = new TypeReference>() {};
Map object = objectMapper.readValue(bytes, typeReference);
- assertEquals(8, object.keySet().size());
+ assertEquals(9, object.keySet().size());
int bitmap = 0;
for (Map.Entry entry : object.entrySet()) {
@@ -117,7 +158,7 @@ else if (k.equals("array")) {
// #7
bitmap |= 1 << 8;
@SuppressWarnings("unchecked")
- List extends Serializable> expected = Arrays.asList((double)Float.MIN_VALUE, null, "array_child_str");
+ List extends Serializable> expected = Arrays.asList((double) Float.MIN_VALUE, null, "array_child_str");
assertEquals(expected, v);
}
else if (k.equals("bool")) {
@@ -125,14 +166,23 @@ else if (k.equals("bool")) {
bitmap |= 1 << 9;
assertEquals(false, v);
}
+ else if (k.equals("ext")) {
+ // #9
+ bitmap |= 1 << 10;
+ MessagePackExtensionType extensionType = (MessagePackExtensionType) v;
+ assertEquals(0, extensionType.getType());
+ assertArrayEquals(extPayload, extensionType.getData());
+ }
}
- assertEquals(0x3FF, bitmap);
+ assertEquals(0x7FF, bitmap);
}
@Test
- public void testParserShouldReadArray() throws IOException {
- MessagePacker packer = new MessagePacker(new OutputStreamBufferOutput(out));
- packer.packArrayHeader(10);
+ public void testParserShouldReadArray()
+ throws IOException
+ {
+ MessagePacker packer = MessagePack.newDefaultPacker(out);
+ packer.packArrayHeader(11);
// #1
packer.packArrayHeader(3);
{
@@ -155,7 +205,7 @@ public void testParserShouldReadArray() throws IOException {
bi = bi.add(BigInteger.ONE);
packer.packBigInteger(bi);
// #8
- byte[] bytes = new byte[]{(byte) 0xFF, (byte) 0xFE, 0x01, 0x00};
+ byte[] bytes = new byte[] {(byte) 0xFF, (byte) 0xFE, 0x01, 0x00};
packer.packBinaryHeader(bytes.length);
packer.writePayload(bytes);
// #9
@@ -168,14 +218,18 @@ public void testParserShouldReadArray() throws IOException {
}
// #10
packer.packBoolean(true);
+ // #11
+ byte[] extPayload = {-80, -50, -25, -114, -25, 16, 60, 68};
+ packer.packExtensionTypeHeader((byte) -1, extPayload.length);
+ packer.writePayload(extPayload);
packer.flush();
bytes = out.toByteArray();
- TypeReference> typeReference = new TypeReference>(){};
+ TypeReference> typeReference = new TypeReference>() {};
List array = objectMapper.readValue(bytes, typeReference);
- assertEquals(10, array.size());
+ assertEquals(11, array.size());
int i = 0;
// #1
@SuppressWarnings("unchecked")
@@ -193,18 +247,18 @@ public void testParserShouldReadArray() throws IOException {
// #4
assertEquals(Long.MIN_VALUE, array.get(i++));
// #5
- assertEquals(Float.MAX_VALUE, (Double)array.get(i++), 0.001f);
+ assertEquals(Float.MAX_VALUE, (Double) array.get(i++), 0.001f);
// #6
- assertEquals(Double.MIN_VALUE, (Double)array.get(i++), 0.001f);
+ assertEquals(Double.MIN_VALUE, (Double) array.get(i++), 0.001f);
// #7
assertEquals(bi, array.get(i++));
// #8
byte[] bs = (byte[]) array.get(i++);
assertEquals(4, bs.length);
- assertEquals((byte)0xFF, bs[0]);
- assertEquals((byte)0xFE, bs[1]);
- assertEquals((byte)0x01, bs[2]);
- assertEquals((byte)0x00, bs[3]);
+ assertEquals((byte) 0xFF, bs[0]);
+ assertEquals((byte) 0xFE, bs[1]);
+ assertEquals((byte) 0x01, bs[2]);
+ assertEquals((byte) 0x00, bs[3]);
// #9
@SuppressWarnings("unchecked")
Map childMap = (Map) array.get(i++);
@@ -223,5 +277,398 @@ else if (k.equals("child_map_age")) {
}
// #10
assertEquals(true, array.get(i++));
+ // #11
+ MessagePackExtensionType extensionType = (MessagePackExtensionType) array.get(i++);
+ assertEquals(-1, extensionType.getType());
+ assertArrayEquals(extPayload, extensionType.getData());
+ }
+
+ @Test
+ public void testMessagePackParserDirectly()
+ throws IOException
+ {
+ MessagePackFactory factory = new MessagePackFactory();
+ File tempFile = File.createTempFile("msgpackTest", "msgpack");
+ tempFile.deleteOnExit();
+
+ FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
+ MessagePacker packer = MessagePack.newDefaultPacker(fileOutputStream);
+ packer.packMapHeader(2);
+ packer.packString("zero");
+ packer.packInt(0);
+ packer.packString("one");
+ packer.packFloat(1.0f);
+ packer.close();
+
+ JsonParser parser = factory.createParser(tempFile);
+ assertTrue(parser instanceof MessagePackParser);
+
+ JsonToken jsonToken = parser.nextToken();
+ assertEquals(JsonToken.START_OBJECT, jsonToken);
+ assertEquals(-1, parser.getTokenLocation().getLineNr());
+ assertEquals(0, parser.getTokenLocation().getColumnNr());
+ assertEquals(-1, parser.getCurrentLocation().getLineNr());
+ assertEquals(1, parser.getCurrentLocation().getColumnNr());
+
+ jsonToken = parser.nextToken();
+ assertEquals(JsonToken.FIELD_NAME, jsonToken);
+ assertEquals("zero", parser.getCurrentName());
+ assertEquals(1, parser.getTokenLocation().getColumnNr());
+ assertEquals(6, parser.getCurrentLocation().getColumnNr());
+
+ jsonToken = parser.nextToken();
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jsonToken);
+ assertEquals(0, parser.getIntValue());
+ assertEquals(6, parser.getTokenLocation().getColumnNr());
+ assertEquals(7, parser.getCurrentLocation().getColumnNr());
+
+ jsonToken = parser.nextToken();
+ assertEquals(JsonToken.FIELD_NAME, jsonToken);
+ assertEquals("one", parser.getCurrentName());
+ assertEquals(7, parser.getTokenLocation().getColumnNr());
+ assertEquals(11, parser.getCurrentLocation().getColumnNr());
+ parser.overrideCurrentName("two");
+ assertEquals("two", parser.getCurrentName());
+
+ jsonToken = parser.nextToken();
+ assertEquals(JsonToken.VALUE_NUMBER_FLOAT, jsonToken);
+ assertEquals(1.0f, parser.getIntValue(), 0.001f);
+ assertEquals(11, parser.getTokenLocation().getColumnNr());
+ assertEquals(16, parser.getCurrentLocation().getColumnNr());
+
+ jsonToken = parser.nextToken();
+ assertEquals(JsonToken.END_OBJECT, jsonToken);
+ assertEquals(-1, parser.getTokenLocation().getLineNr());
+ assertEquals(16, parser.getTokenLocation().getColumnNr());
+ assertEquals(-1, parser.getCurrentLocation().getLineNr());
+ assertEquals(16, parser.getCurrentLocation().getColumnNr());
+
+ assertNull(parser.nextToken());
+
+ parser.close();
+ parser.close(); // Intentional
+ }
+
+ @Test
+ public void testReadPrimitives()
+ throws Exception
+ {
+ MessagePackFactory factory = new MessagePackFactory();
+ File tempFile = createTempFile();
+
+ FileOutputStream out = new FileOutputStream(tempFile);
+ MessagePacker packer = MessagePack.newDefaultPacker(out);
+ packer.packString("foo");
+ packer.packDouble(3.14);
+ packer.packLong(Long.MAX_VALUE);
+ byte[] bytes = {0x00, 0x11, 0x22};
+ packer.packBinaryHeader(bytes.length);
+ packer.writePayload(bytes);
+ packer.close();
+
+ JsonParser parser = factory.createParser(new FileInputStream(tempFile));
+ assertEquals(JsonToken.VALUE_STRING, parser.nextToken());
+ assertEquals("foo", parser.getText());
+ assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken());
+ assertEquals(3.14, parser.getDoubleValue(), 0.0001);
+ assertEquals(JsonToken.VALUE_NUMBER_INT, parser.nextToken());
+ assertEquals(Long.MAX_VALUE, parser.getLongValue());
+ assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, parser.nextToken());
+ assertEquals(bytes.length, parser.getBinaryValue().length);
+ assertEquals(bytes[0], parser.getBinaryValue()[0]);
+ assertEquals(bytes[1], parser.getBinaryValue()[1]);
+ assertEquals(bytes[2], parser.getBinaryValue()[2]);
+ }
+
+ @Test
+ public void testBigDecimal()
+ throws IOException
+ {
+ double d0 = 1.23456789;
+ double d1 = 1.23450000000000000000006789;
+ MessagePacker packer = MessagePack.newDefaultPacker(out);
+ packer.packArrayHeader(5);
+ packer.packDouble(d0);
+ packer.packDouble(d1);
+ packer.packDouble(Double.MIN_VALUE);
+ packer.packDouble(Double.MAX_VALUE);
+ packer.packDouble(Double.MIN_NORMAL);
+ packer.flush();
+
+ ObjectMapper mapper = new ObjectMapper(new MessagePackFactory());
+ mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
+ List objects = mapper.readValue(out.toByteArray(), new TypeReference>() {});
+ assertEquals(5, objects.size());
+ int idx = 0;
+ assertEquals(BigDecimal.valueOf(d0), objects.get(idx++));
+ assertEquals(BigDecimal.valueOf(d1), objects.get(idx++));
+ assertEquals(BigDecimal.valueOf(Double.MIN_VALUE), objects.get(idx++));
+ assertEquals(BigDecimal.valueOf(Double.MAX_VALUE), objects.get(idx++));
+ assertEquals(BigDecimal.valueOf(Double.MIN_NORMAL), objects.get(idx++));
+ }
+
+ private File createTestFile()
+ throws Exception
+ {
+ File tempFile = createTempFile(new FileSetup()
+ {
+ @Override
+ public void setup(File f)
+ throws IOException
+ {
+ MessagePack.newDefaultPacker(new FileOutputStream(f))
+ .packArrayHeader(1).packInt(1)
+ .packArrayHeader(1).packInt(1)
+ .close();
+ }
+ });
+ return tempFile;
+ }
+
+ @Test(expected = IOException.class)
+ public void testEnableFeatureAutoCloseSource()
+ throws Exception
+ {
+ File tempFile = createTestFile();
+ MessagePackFactory factory = new MessagePackFactory();
+ FileInputStream in = new FileInputStream(tempFile);
+ ObjectMapper objectMapper = new ObjectMapper(factory);
+ objectMapper.readValue(in, new TypeReference>() {});
+ objectMapper.readValue(in, new TypeReference>() {});
+ }
+
+ @Test
+ public void testDisableFeatureAutoCloseSource()
+ throws Exception
+ {
+ File tempFile = createTestFile();
+ FileInputStream in = new FileInputStream(tempFile);
+ ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ objectMapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false);
+ objectMapper.readValue(in, new TypeReference>() {});
+ objectMapper.readValue(in, new TypeReference>() {});
+ }
+
+ @Test
+ public void testParseBigDecimal()
+ throws IOException
+ {
+ ArrayList list = new ArrayList();
+ list.add(new BigDecimal(Long.MAX_VALUE));
+ ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ byte[] bytes = objectMapper.writeValueAsBytes(list);
+
+ ArrayList result = objectMapper.readValue(
+ bytes, new TypeReference>() {});
+ assertEquals(list, result);
+ }
+
+ @Test
+ public void testReadPrimitiveObjectViaObjectMapper()
+ throws Exception
+ {
+ File tempFile = createTempFile();
+ FileOutputStream out = new FileOutputStream(tempFile);
+
+ MessagePacker packer = MessagePack.newDefaultPacker(out);
+ packer.packString("foo");
+ packer.packLong(Long.MAX_VALUE);
+ packer.packDouble(3.14);
+ byte[] bytes = {0x00, 0x11, 0x22};
+ packer.packBinaryHeader(bytes.length);
+ packer.writePayload(bytes);
+ packer.close();
+
+ FileInputStream in = new FileInputStream(tempFile);
+ ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ objectMapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false);
+ assertEquals("foo", objectMapper.readValue(in, new TypeReference() {}));
+ long l = objectMapper.readValue(in, new TypeReference() {});
+ assertEquals(Long.MAX_VALUE, l);
+ double d = objectMapper.readValue(in, new TypeReference() {});
+ assertEquals(3.14, d, 0.001);
+ byte[] bs = objectMapper.readValue(in, new TypeReference() {});
+ assertEquals(bytes.length, bs.length);
+ assertEquals(bytes[0], bs[0]);
+ assertEquals(bytes[1], bs[1]);
+ assertEquals(bytes[2], bs[2]);
+ }
+
+ @Test
+ public void testBinaryKey()
+ throws Exception
+ {
+ File tempFile = createTempFile();
+ FileOutputStream out = new FileOutputStream(tempFile);
+ MessagePacker packer = MessagePack.newDefaultPacker(out);
+ packer.packMapHeader(2);
+ packer.packString("foo");
+ packer.packDouble(3.14);
+ byte[] bytes = "bar".getBytes();
+ packer.packBinaryHeader(bytes.length);
+ packer.writePayload(bytes);
+ packer.packLong(42);
+ packer.close();
+
+ ObjectMapper mapper = new ObjectMapper(new MessagePackFactory());
+ Map object = mapper.readValue(new FileInputStream(tempFile), new TypeReference>() {});
+ assertEquals(2, object.size());
+ assertEquals(3.14, object.get("foo"));
+ assertEquals(42, object.get("bar"));
+ }
+
+ @Test
+ public void testBinaryKeyInNestedObject()
+ throws Exception
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ MessagePacker packer = MessagePack.newDefaultPacker(out);
+ packer.packArrayHeader(2);
+ packer.packMapHeader(1);
+ byte[] bytes = "bar".getBytes();
+ packer.packBinaryHeader(bytes.length);
+ packer.writePayload(bytes);
+ packer.packInt(12);
+ packer.packInt(1);
+ packer.close();
+
+ ObjectMapper mapper = new ObjectMapper(new MessagePackFactory());
+ List objects = mapper.readValue(out.toByteArray(), new TypeReference>() {});
+ assertEquals(2, objects.size());
+ @SuppressWarnings(value = "unchecked")
+ Map map = (Map) objects.get(0);
+ assertEquals(1, map.size());
+ assertEquals(12, map.get("bar"));
+ assertEquals(1, objects.get(1));
+ }
+
+ @Test
+ public void testByteArrayKey()
+ throws IOException
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ MessagePacker messagePacker = MessagePack.newDefaultPacker(out).packMapHeader(2);
+ byte[] k0 = new byte[] {0};
+ byte[] k1 = new byte[] {1};
+ messagePacker.packBinaryHeader(1).writePayload(k0).packInt(2);
+ messagePacker.packBinaryHeader(1).writePayload(k1).packInt(3);
+ messagePacker.close();
+
+ ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ SimpleModule module = new SimpleModule();
+ module.addKeyDeserializer(byte[].class, new KeyDeserializer()
+ {
+ @Override
+ public Object deserializeKey(String key, DeserializationContext ctxt)
+ throws IOException, JsonProcessingException
+ {
+ return key.getBytes();
+ }
+ });
+ objectMapper.registerModule(module);
+
+ Map map = objectMapper.readValue(
+ out.toByteArray(), new TypeReference>() {});
+ assertEquals(2, map.size());
+ for (Map.Entry entry : map.entrySet()) {
+ if (Arrays.equals(entry.getKey(), k0)) {
+ assertEquals((Integer) 2, entry.getValue());
+ }
+ else if (Arrays.equals(entry.getKey(), k1)) {
+ assertEquals((Integer) 3, entry.getValue());
+ }
+ }
+ }
+
+ @Test
+ public void testIntegerKey()
+ throws IOException
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ MessagePacker messagePacker = MessagePack.newDefaultPacker(out).packMapHeader(3);
+ for (int i = 0; i < 2; i++) {
+ messagePacker.packInt(i).packInt(i + 2);
+ }
+ messagePacker.close();
+
+ ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ SimpleModule module = new SimpleModule();
+ module.addKeyDeserializer(Integer.class, new KeyDeserializer()
+ {
+ @Override
+ public Object deserializeKey(String key, DeserializationContext ctxt)
+ throws IOException, JsonProcessingException
+ {
+ return Integer.valueOf(key);
+ }
+ });
+ objectMapper.registerModule(module);
+
+ Map map = objectMapper.readValue(
+ out.toByteArray(), new TypeReference>() {});
+ assertEquals(2, map.size());
+ assertEquals((Integer) 2, map.get(0));
+ assertEquals((Integer) 3, map.get(1));
+ }
+
+ @Test
+ public void testFloatKey()
+ throws IOException
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ MessagePacker messagePacker = MessagePack.newDefaultPacker(out).packMapHeader(3);
+ for (int i = 0; i < 2; i++) {
+ messagePacker.packFloat(i).packInt(i + 2);
+ }
+ messagePacker.close();
+
+ ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ SimpleModule module = new SimpleModule();
+ module.addKeyDeserializer(Float.class, new KeyDeserializer()
+ {
+ @Override
+ public Object deserializeKey(String key, DeserializationContext ctxt)
+ throws IOException, JsonProcessingException
+ {
+ return Float.valueOf(key);
+ }
+ });
+ objectMapper.registerModule(module);
+
+ Map map = objectMapper.readValue(
+ out.toByteArray(), new TypeReference>() {});
+ assertEquals(2, map.size());
+ assertEquals((Integer) 2, map.get(0f));
+ assertEquals((Integer) 3, map.get(1f));
+ }
+
+ @Test
+ public void testBooleanKey()
+ throws IOException
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ MessagePacker messagePacker = MessagePack.newDefaultPacker(out).packMapHeader(3);
+ messagePacker.packBoolean(true).packInt(2);
+ messagePacker.packBoolean(false).packInt(3);
+ messagePacker.close();
+
+ ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
+ SimpleModule module = new SimpleModule();
+ module.addKeyDeserializer(Boolean.class, new KeyDeserializer()
+ {
+ @Override
+ public Object deserializeKey(String key, DeserializationContext ctxt)
+ throws IOException, JsonProcessingException
+ {
+ return Boolean.valueOf(key);
+ }
+ });
+ objectMapper.registerModule(module);
+
+ Map map = objectMapper.readValue(
+ out.toByteArray(), new TypeReference>() {});
+ assertEquals(2, map.size());
+ assertEquals((Integer) 2, map.get(true));
+ assertEquals((Integer) 3, map.get(false));
}
}
diff --git a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/Benchmarker.java b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/Benchmarker.java
new file mode 100644
index 000000000..980348024
--- /dev/null
+++ b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/Benchmarker.java
@@ -0,0 +1,98 @@
+//
+// MessagePack for Java
+//
+// Licensed 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 org.msgpack.jackson.dataformat.benchmark;
+
+import org.apache.commons.math3.stat.StatUtils;
+import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class Benchmarker
+{
+ private final List benchmarkableList = new ArrayList();
+
+ public abstract static class Benchmarkable
+ {
+ private final String label;
+
+ protected Benchmarkable(String label)
+ {
+ this.label = label;
+ }
+
+ public abstract void run() throws Exception;
+ }
+
+ public void addBenchmark(Benchmarkable benchmark)
+ {
+ benchmarkableList.add(benchmark);
+ }
+
+ private static class Tuple
+ {
+ F first;
+ S second;
+
+ public Tuple(F first, S second)
+ {
+ this.first = first;
+ this.second = second;
+ }
+ }
+
+ public void run(int count, int warmupCount)
+ throws Exception
+ {
+ List> benchmarksResults = new ArrayList>(benchmarkableList.size());
+ for (Benchmarkable benchmark : benchmarkableList) {
+ benchmarksResults.add(new Tuple(benchmark.label, new double[count]));
+ }
+
+ for (int i = 0; i < count + warmupCount; i++) {
+ for (int bi = 0; bi < benchmarkableList.size(); bi++) {
+ Benchmarkable benchmark = benchmarkableList.get(bi);
+ long currentTimeNanos = System.nanoTime();
+ benchmark.run();
+
+ if (i >= warmupCount) {
+ benchmarksResults.get(bi).second[i - warmupCount] = (System.nanoTime() - currentTimeNanos) / 1000000.0;
+ }
+ }
+ }
+
+ for (Tuple benchmarkResult : benchmarksResults) {
+ printStat(benchmarkResult.first, benchmarkResult.second);
+ }
+ }
+
+ private void printStat(String label, double[] origValues)
+ {
+ double[] values = origValues;
+ Arrays.sort(origValues);
+ if (origValues.length > 2) {
+ values = Arrays.copyOfRange(origValues, 1, origValues.length - 1);
+ }
+ StandardDeviation standardDeviation = new StandardDeviation();
+ System.out.println(label + ":");
+ System.out.println(String.format(" mean : %8.3f", StatUtils.mean(values)));
+ System.out.println(String.format(" min : %8.3f", StatUtils.min(values)));
+ System.out.println(String.format(" max : %8.3f", StatUtils.max(values)));
+ System.out.println(String.format(" stdev: %8.3f", standardDeviation.evaluate(values)));
+ System.out.println("");
+ }
+}
diff --git a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/MessagePackDataformatHugeDataBenchmarkTest.java b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/MessagePackDataformatHugeDataBenchmarkTest.java
index 52a2203bc..b3a159111 100644
--- a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/MessagePackDataformatHugeDataBenchmarkTest.java
+++ b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/MessagePackDataformatHugeDataBenchmarkTest.java
@@ -15,31 +15,37 @@
//
package org.msgpack.jackson.dataformat.benchmark;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
-import org.msgpack.jackson.dataformat.MessagePackDataformatTestBase;
import org.msgpack.jackson.dataformat.MessagePackFactory;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
-public class MessagePackDataformatHugeDataBenchmarkTest extends MessagePackDataformatTestBase {
- private static final int ELM_NUM = 1500000;
- private static final int SAMPLING_COUNT = 4;
+public class MessagePackDataformatHugeDataBenchmarkTest
+{
+ private static final int ELM_NUM = 100000;
+ private static final int COUNT = 6;
+ private static final int WARMUP_COUNT = 4;
private final ObjectMapper origObjectMapper = new ObjectMapper();
private final ObjectMapper msgpackObjectMapper = new ObjectMapper(new MessagePackFactory());
private static final List value;
private static final byte[] packedByOriginal;
private static final byte[] packedByMsgPack;
+
static {
value = new ArrayList();
for (int i = 0; i < ELM_NUM; i++) {
- value.add((long)i);
+ value.add((long) i);
}
for (int i = 0; i < ELM_NUM; i++) {
- value.add((double)i);
+ value.add((double) i);
}
for (int i = 0; i < ELM_NUM; i++) {
value.add(String.valueOf(i));
@@ -48,45 +54,83 @@ public class MessagePackDataformatHugeDataBenchmarkTest extends MessagePackDataf
byte[] bytes = null;
try {
bytes = new ObjectMapper().writeValueAsBytes(value);
- } catch (JsonProcessingException e) {
+ }
+ catch (JsonProcessingException e) {
e.printStackTrace();
}
packedByOriginal = bytes;
try {
bytes = new ObjectMapper(new MessagePackFactory()).writeValueAsBytes(value);
- } catch (JsonProcessingException e) {
+ }
+ catch (JsonProcessingException e) {
e.printStackTrace();
}
packedByMsgPack = bytes;
}
+ public MessagePackDataformatHugeDataBenchmarkTest()
+ {
+ origObjectMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
+ msgpackObjectMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
+ }
+
@Test
- public void testBenchmark() throws Exception {
- double durationOfSerializeWithJson[] = new double[SAMPLING_COUNT];
- double durationOfSerializeWithMsgPack[] = new double[SAMPLING_COUNT];
- double durationOfDeserializeWithJson[] = new double[SAMPLING_COUNT];
- double durationOfDeserializeWithMsgPack[] = new double[SAMPLING_COUNT];
- for (int si = 0; si < SAMPLING_COUNT; si++) {
- long currentTimeMillis = System.currentTimeMillis();
- origObjectMapper.writeValueAsBytes(value);
- durationOfSerializeWithJson[si] = System.currentTimeMillis() - currentTimeMillis;
+ public void testBenchmark()
+ throws Exception
+ {
+ Benchmarker benchmarker = new Benchmarker();
+
+ File tempFileJackson = File.createTempFile("msgpack-jackson-", "-huge-jackson");
+ tempFileJackson.deleteOnExit();
+ final OutputStream outputStreamJackson = new FileOutputStream(tempFileJackson);
- currentTimeMillis = System.currentTimeMillis();
- msgpackObjectMapper.writeValueAsBytes(value);
- durationOfSerializeWithMsgPack[si] = System.currentTimeMillis() - currentTimeMillis;
+ File tempFileMsgpack = File.createTempFile("msgpack-jackson-", "-huge-msgpack");
+ tempFileMsgpack.deleteOnExit();
+ final OutputStream outputStreamMsgpack = new FileOutputStream(tempFileMsgpack);
- currentTimeMillis = System.currentTimeMillis();
- origObjectMapper.readValue(packedByOriginal, new TypeReference>() {});
- durationOfDeserializeWithJson[si] = System.currentTimeMillis() - currentTimeMillis;
+ benchmarker.addBenchmark(new Benchmarker.Benchmarkable("serialize(huge) with JSON") {
+ @Override
+ public void run()
+ throws Exception
+ {
+ origObjectMapper.writeValue(outputStreamJackson, value);
+ }
+ });
- currentTimeMillis = System.currentTimeMillis();
- msgpackObjectMapper.readValue(packedByMsgPack, new TypeReference>() {});
- durationOfDeserializeWithMsgPack[si] = System.currentTimeMillis() - currentTimeMillis;
+ benchmarker.addBenchmark(new Benchmarker.Benchmarkable("serialize(huge) with MessagePack") {
+ @Override
+ public void run()
+ throws Exception
+ {
+ msgpackObjectMapper.writeValue(outputStreamMsgpack, value);
+ }
+ });
+
+ benchmarker.addBenchmark(new Benchmarker.Benchmarkable("deserialize(huge) with JSON") {
+ @Override
+ public void run()
+ throws Exception
+ {
+ origObjectMapper.readValue(packedByOriginal, new TypeReference>() {});
+ }
+ });
+
+ benchmarker.addBenchmark(new Benchmarker.Benchmarkable("deserialize(huge) with MessagePack") {
+ @Override
+ public void run()
+ throws Exception
+ {
+ msgpackObjectMapper.readValue(packedByMsgPack, new TypeReference>() {});
+ }
+ });
+
+ try {
+ benchmarker.run(COUNT, WARMUP_COUNT);
+ }
+ finally {
+ outputStreamJackson.close();
+ outputStreamMsgpack.close();
}
- printStat("serialize(huge) with JSON", durationOfSerializeWithJson);
- printStat("serialize(huge) with MessagePack", durationOfSerializeWithMsgPack);
- printStat("deserialize(huge) with JSON", durationOfDeserializeWithJson);
- printStat("deserialize(huge) with MessagePack", durationOfDeserializeWithMsgPack);
}
}
diff --git a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/MessagePackDataformatPojoBenchmarkTest.java b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/MessagePackDataformatPojoBenchmarkTest.java
index 70401fffd..179b09891 100644
--- a/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/MessagePackDataformatPojoBenchmarkTest.java
+++ b/msgpack-jackson/src/test/java/org/msgpack/jackson/dataformat/benchmark/MessagePackDataformatPojoBenchmarkTest.java
@@ -15,29 +15,38 @@
//
package org.msgpack.jackson.dataformat.benchmark;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
-import org.msgpack.jackson.dataformat.MessagePackDataformatTestBase;
import org.msgpack.jackson.dataformat.MessagePackFactory;
+import static org.msgpack.jackson.dataformat.MessagePackDataformatTestBase.NormalPojo;
+import static org.msgpack.jackson.dataformat.MessagePackDataformatTestBase.Suit;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
-public class MessagePackDataformatPojoBenchmarkTest extends MessagePackDataformatTestBase {
- private static final int LOOP_MAX = 8000;
- private static final int LOOP_FACTOR = 50;
- private static final int SAMPLING_COUNT = 4;
- private static final List pojos = new ArrayList(LOOP_MAX);
- private static final List pojosSerWithOrig = new ArrayList(LOOP_MAX);
- private static final List pojosSerWithMsgPack = new ArrayList(LOOP_MAX);
+public class MessagePackDataformatPojoBenchmarkTest
+{
+ private static final int LOOP_MAX = 200;
+ private static final int LOOP_FACTOR_SER = 40;
+ private static final int LOOP_FACTOR_DESER = 200;
+ private static final int COUNT = 6;
+ private static final int WARMUP_COUNT = 4;
+ private final List pojos = new ArrayList(LOOP_MAX);
+ private final List pojosSerWithOrig = new ArrayList(LOOP_MAX);
+ private final List pojosSerWithMsgPack = new ArrayList(LOOP_MAX);
private final ObjectMapper origObjectMapper = new ObjectMapper();
private final ObjectMapper msgpackObjectMapper = new ObjectMapper(new MessagePackFactory());
- static {
- final ObjectMapper origObjectMapper = new ObjectMapper();
- final ObjectMapper msgpackObjectMapper = new ObjectMapper(new MessagePackFactory());
+ public MessagePackDataformatPojoBenchmarkTest()
+ {
+ origObjectMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
+ msgpackObjectMapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
for (int i = 0; i < LOOP_MAX; i++) {
NormalPojo pojo = new NormalPojo();
@@ -45,7 +54,11 @@ public class MessagePackDataformatPojoBenchmarkTest extends MessagePackDataforma
pojo.l = i;
pojo.f = Float.valueOf(i);
pojo.d = Double.valueOf(i);
- pojo.setS(String.valueOf(i));
+ StringBuilder sb = new StringBuilder();
+ for (int sbi = 0; sbi < i * 50; sbi++) {
+ sb.append("x");
+ }
+ pojo.setS(sb.toString());
pojo.bool = i % 2 == 0;
pojo.bi = BigInteger.valueOf(i);
switch (i % 4) {
@@ -69,58 +82,94 @@ public class MessagePackDataformatPojoBenchmarkTest extends MessagePackDataforma
for (int i = 0; i < LOOP_MAX; i++) {
try {
pojosSerWithOrig.add(origObjectMapper.writeValueAsBytes(pojos.get(i)));
- } catch (JsonProcessingException e) {
- e.printStackTrace();
+ }
+ catch (JsonProcessingException e) {
+ throw new RuntimeException("Failed to create test data");
}
}
for (int i = 0; i < LOOP_MAX; i++) {
try {
pojosSerWithMsgPack.add(msgpackObjectMapper.writeValueAsBytes(pojos.get(i)));
- } catch (JsonProcessingException e) {
- e.printStackTrace();
+ }
+ catch (JsonProcessingException e) {
+ throw new RuntimeException("Failed to create test data");
}
}
}
@Test
- public void testBenchmark() throws Exception {
- double durationOfSerializeWithJson[] = new double[SAMPLING_COUNT];
- double durationOfSerializeWithMsgPack[] = new double[SAMPLING_COUNT];
- double durationOfDeserializeWithJson[] = new double[SAMPLING_COUNT];
- double durationOfDeserializeWithMsgPack[] = new double[SAMPLING_COUNT];
- for (int si = 0; si < SAMPLING_COUNT; si++) {
- long currentTimeMillis = System.currentTimeMillis();
- for (int j = 0; j < LOOP_FACTOR; j++)
- for (int i = 0; i < LOOP_MAX; i++) {
- origObjectMapper.writeValueAsBytes(pojos.get(i));
+ public void testBenchmark()
+ throws Exception
+ {
+ Benchmarker benchmarker = new Benchmarker();
+
+ File tempFileJackson = File.createTempFile("msgpack-jackson-", "-huge-jackson");
+ tempFileJackson.deleteOnExit();
+ final OutputStream outputStreamJackson = new FileOutputStream(tempFileJackson);
+
+ File tempFileMsgpack = File.createTempFile("msgpack-jackson-", "-huge-msgpack");
+ tempFileMsgpack.deleteOnExit();
+ final OutputStream outputStreamMsgpack = new FileOutputStream(tempFileMsgpack);
+
+ benchmarker.addBenchmark(new Benchmarker.Benchmarkable("serialize(pojo) with JSON") {
+ @Override
+ public void run()
+ throws Exception
+ {
+ for (int j = 0; j < LOOP_FACTOR_SER; j++) {
+ for (int i = 0; i < LOOP_MAX; i++) {
+ origObjectMapper.writeValue(outputStreamJackson, pojos.get(i));
+ }
}
- durationOfSerializeWithJson[si] = System.currentTimeMillis() - currentTimeMillis;
+ }
+ });
- currentTimeMillis = System.currentTimeMillis();
- for (int j = 0; j < LOOP_FACTOR; j++)
- for (int i = 0; i < LOOP_MAX; i++) {
- msgpackObjectMapper.writeValueAsBytes(pojos.get(i));
+ benchmarker.addBenchmark(new Benchmarker.Benchmarkable("serialize(pojo) with MessagePack") {
+ @Override
+ public void run()
+ throws Exception
+ {
+ for (int j = 0; j < LOOP_FACTOR_SER; j++) {
+ for (int i = 0; i < LOOP_MAX; i++) {
+ msgpackObjectMapper.writeValue(outputStreamMsgpack, pojos.get(i));
+ }
}
- durationOfSerializeWithMsgPack[si] = System.currentTimeMillis() - currentTimeMillis;
+ }
+ });
- currentTimeMillis = System.currentTimeMillis();
- for (int j = 0; j < LOOP_FACTOR; j++)
- for (int i = 0; i < LOOP_MAX; i++) {
- origObjectMapper.readValue(pojosSerWithOrig.get(i), NormalPojo.class);
+ benchmarker.addBenchmark(new Benchmarker.Benchmarkable("deserialize(pojo) with JSON") {
+ @Override
+ public void run()
+ throws Exception
+ {
+ for (int j = 0; j < LOOP_FACTOR_DESER; j++) {
+ for (int i = 0; i < LOOP_MAX; i++) {
+ origObjectMapper.readValue(pojosSerWithOrig.get(i), NormalPojo.class);
+ }
}
- durationOfDeserializeWithJson[si] = System.currentTimeMillis() - currentTimeMillis;
+ }
+ });
- currentTimeMillis = System.currentTimeMillis();
- for (int j = 0; j < LOOP_FACTOR; j++)
- for (int i = 0; i < LOOP_MAX; i++) {
- msgpackObjectMapper.readValue(pojosSerWithMsgPack.get(i), NormalPojo.class);
+ benchmarker.addBenchmark(new Benchmarker.Benchmarkable("deserialize(pojo) with MessagePack") {
+ @Override
+ public void run()
+ throws Exception
+ {
+ for (int j = 0; j < LOOP_FACTOR_DESER; j++) {
+ for (int i = 0; i < LOOP_MAX; i++) {
+ msgpackObjectMapper.readValue(pojosSerWithMsgPack.get(i), NormalPojo.class);
+ }
}
- durationOfDeserializeWithMsgPack[si] = System.currentTimeMillis() - currentTimeMillis;
+ }
+ });
+
+ try {
+ benchmarker.run(COUNT, WARMUP_COUNT);
+ }
+ finally {
+ outputStreamJackson.close();
+ outputStreamMsgpack.close();
}
- printStat("serialize(pojo) with JSON", durationOfSerializeWithJson);
- printStat("serialize(pojo) with MessagePack", durationOfSerializeWithMsgPack);
- printStat("deserialize(pojo) with JSON", durationOfDeserializeWithJson);
- printStat("deserialize(pojo) with MessagePack", durationOfDeserializeWithMsgPack);
}
}
diff --git a/project/Build.scala b/project/Build.scala
deleted file mode 100644
index e291b7d85..000000000
--- a/project/Build.scala
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright 2012 Taro L. Saito
- *
- * Licensed 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.
- */
-
-
-import de.johoop.findbugs4sbt.ReportType
-import sbt._
-import Keys._
-import de.johoop.findbugs4sbt.FindBugs._
-import de.johoop.jacoco4sbt._
-import JacocoPlugin._
-import sbtrelease.ReleasePlugin._
-import xerial.sbt.Sonatype._
-
-object Build extends Build {
-
- val SCALA_VERSION = "2.11.1"
-
- lazy val buildSettings = Defaults.coreDefaultSettings ++
- releaseSettings ++
- findbugsSettings ++
- jacoco.settings ++
- Seq[Setting[_]](
- organization := "org.msgpack",
- organizationName := "MessagePack",
- organizationHomepage := Some(new URL("http://msgpack.org/")),
- description := "MessagePack for Java",
- scalaVersion in Global := SCALA_VERSION,
- logBuffered in Test := false,
- //parallelExecution in Test := false,
- autoScalaLibrary := false,
- crossPaths := false,
- concurrentRestrictions in Global := Seq(
- Tags.limit(Tags.Test, 1)
- ),
- publishTo := {
- val nexus = "https://oss.sonatype.org/"
- if (isSnapshot.value)
- Some("snapshots" at nexus + "content/repositories/snapshots")
- else
- Some("releases" at nexus + "service/local/staging/deploy/maven2")
- },
- parallelExecution in jacoco.Config := false,
- // Since sbt-0.13.2
- incOptions := incOptions.value.withNameHashing(true),
- //resolvers += Resolver.mavenLocal,
- scalacOptions ++= Seq("-encoding", "UTF-8", "-deprecation", "-unchecked", "-target:jvm-1.6", "-feature"),
- javaOptions in Test ++= Seq("-ea"),
- javacOptions in (Compile, compile) ++= Seq("-encoding", "UTF-8", "-Xlint:unchecked", "-Xlint:deprecation", "-source", "1.6", "-target", "1.6"),
- javacOptions in doc := Seq("-source", "1.6", "-Xdoclint:none"),
- findbugsReportType := Some(ReportType.FancyHtml),
- findbugsReportPath := Some(crossTarget.value / "findbugs" / "report.html"),
- pomExtra := {
- http://msgpack.org/
-
-