/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* 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 com.hazelcast.nio.serialization;
import com.hazelcast.internal.serialization.InternalSerializationService;
import com.hazelcast.internal.serialization.impl.DefaultSerializationServiceBuilder;
import com.hazelcast.internal.serialization.impl.HeapData;
import com.hazelcast.internal.serialization.impl.SerializationConstants;
import com.hazelcast.nio.Bits;
import com.hazelcast.nio.BufferObjectDataInput;
import com.hazelcast.nio.BufferObjectDataOutput;
import com.hazelcast.test.HazelcastParallelClassRunner;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(HazelcastParallelClassRunner.class)
@Category(QuickTest.class)
public class StringSerializationTest {
private InternalSerializationService serializationService;
private static final String TEST_DATA_TURKISH = "Pijamalı hasta, yağız şoföre çabucak güvendi.";
private static final String TEST_DATA_JAPANESE = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム";
private static final String TEST_DATA_ASCII = "The quick brown fox jumps over the lazy dog";
private static final String TEST_DATA_ALL = TEST_DATA_TURKISH + TEST_DATA_JAPANESE + TEST_DATA_ASCII;
private static final int TEST_STR_SIZE = 1 << 20;
private static final byte[] TEST_DATA_BYTES_ALL = TEST_DATA_ALL.getBytes(Charset.forName("utf8"));
private static final char[] allChars;
static {
CharBuffer cb = CharBuffer.allocate(Character.MAX_VALUE);
for (char c = 0; c < Character.MAX_VALUE; c++) {
if (Character.isLetter(c)) {
cb.append(c);
}
}
allChars = cb.array();
}
@Before
public void setup() {
serializationService = new DefaultSerializationServiceBuilder().build();
}
@After
public void tearDown() {
serializationService.dispose();
}
@Test
public void testStringEncode() {
byte[] expected = toDataByte(TEST_DATA_BYTES_ALL, TEST_DATA_ALL.length());
byte[] actual = serializationService.toBytes(TEST_DATA_ALL);
assertArrayEquals(expected, actual);
}
@Test
public void testStringDecode() {
Data data = new HeapData(toDataByte(TEST_DATA_BYTES_ALL, TEST_DATA_ALL.length()));
String actualStr = serializationService.toObject(data);
assertEquals(TEST_DATA_ALL, actualStr);
}
@Test
public void testStringAllCharLetterEncode() {
String allStr = new String(allChars);
byte[] expected = allStr.getBytes(Charset.forName("utf8"));
byte[] bytes = serializationService.toBytes(allStr);
byte[] actual = Arrays.copyOfRange(bytes, HeapData.DATA_OFFSET + Bits.INT_SIZE_IN_BYTES, bytes.length);
assertArrayEquals(expected, actual);
}
@Test
public void testLargeStringEncodeDecode() {
StringBuilder sb = new StringBuilder();
int i = 0, j = 0;
while (j < TEST_STR_SIZE) {
int ch = i++ % Character.MAX_VALUE;
if (Character.isLetter(ch)) {
sb.append(ch);
j++;
}
}
String actualStr = sb.toString();
byte[] strBytes = actualStr.getBytes(Charset.forName("utf8"));
byte[] actualDataBytes = serializationService.toBytes(actualStr);
byte[] expectedDataByte = toDataByte(strBytes, actualStr.length());
String decodedStr = serializationService.toObject(new HeapData(expectedDataByte));
assertArrayEquals("Deserialized byte array do not match utf-8 encoding", expectedDataByte, actualDataBytes);
assertEquals(decodedStr, actualStr);
}
@Test
public void testNullStringEncodeDecode() {
Data nullData = serializationService.toData(null);
String decodedStr = serializationService.toObject(nullData);
assertNull(decodedStr);
}
@Test
public void testNullStringEncodeDecode2() throws Exception {
BufferObjectDataOutput objectDataOutput = serializationService.createObjectDataOutput();
objectDataOutput.writeUTF(null);
byte[] bytes = objectDataOutput.toByteArray();
objectDataOutput.close();
BufferObjectDataInput objectDataInput = serializationService.createObjectDataInput(bytes);
String decodedStr = objectDataInput.readUTF();
assertNull(decodedStr);
}
@Test
public void testStringAllCharLetterDecode() {
String allStr = new String(allChars);
byte[] expected = allStr.getBytes(Charset.forName("utf8"));
Data data = new HeapData(toDataByte(expected, allStr.length()));
String actualStr = serializationService.toObject(data);
assertEquals(allStr, actualStr);
}
@Test
public void testStringArrayEncodeDecode() {
String[] stringArray = new String[100];
for (int i = 0; i < stringArray.length; i++) {
stringArray[i] = TEST_DATA_ALL + i;
}
Data dataStrArray = serializationService.toData(stringArray);
String[] actualStr = serializationService.toObject(dataStrArray);
assertEquals(SerializationConstants.CONSTANT_TYPE_STRING_ARRAY, dataStrArray.getType());
assertArrayEquals(stringArray, actualStr);
}
private byte[] toDataByte(byte[] input, int length) {
// the first 4 byte of type id, 4 byte string length and last 4 byte of partition hashCode
ByteBuffer bf = ByteBuffer.allocate(input.length + 12);
bf.putInt(0);
bf.putInt(SerializationConstants.CONSTANT_TYPE_STRING);
bf.putInt(length);
bf.put(input);
return bf.array();
}
}