Skip to content

Commit

Permalink
fix: utf 16 support
Browse files Browse the repository at this point in the history
  • Loading branch information
lo1nt committed Nov 10, 2023
1 parent 01df07c commit 5e2dfc2
Show file tree
Hide file tree
Showing 6 changed files with 619 additions and 239 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,38 @@ public class AbstractStringBuilderDeserializer extends JavaDeserializer {
private static final Logger log = Logger.getLogger(AbstractStringBuilderDeserializer.class.getName());

private static final boolean ENABLE = judgeAvailability();
/** String 的 value field, 用以判断是否需要用当前 deserializer */
private static Field stringValueField;
/** String 的 coder field, 用以从中间 String 变量中获取 coder */
private static Field stringCoderField;

static {
try {
stringCoderField = String.class.getDeclaredField("coder");
stringCoderField.setAccessible(true);

Check warning on line 31 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L31

Added line #L31 was not covered by tests
} catch (Throwable t) {
log.log(Level.WARNING,
"coder field not found or not accessible, will skip coder check, error is " + t.getMessage());
}

Check warning on line 35 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L35

Added line #L35 was not covered by tests
}

/**
* 判断是否要使用该反序列化器, 当 String.value 类型为 byte[] 时需要使用
* 判断是否要使用该反序列化器, 当 String.value 类型不为 char[] 时需要使用
* @return
*/
private static boolean judgeAvailability() {
Field field;
try {
field = String.class.getDeclaredField("value");
stringValueField = String.class.getDeclaredField("value");
stringValueField.setAccessible(true);
} catch (Throwable t) {
return false;

Check warning on line 47 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L46-L47

Added lines #L46 - L47 were not covered by tests
}

if (byte[].class.equals(field.getType())) {
return true;
if (char[].class.equals(stringValueField.getType())) {
return false;
}

return false;
return true;

Check warning on line 54 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L54

Added line #L54 was not covered by tests
}

public static boolean isEnable() {
Expand All @@ -52,27 +66,57 @@ public AbstractStringBuilderDeserializer(Class<?> cl) {
protected HashMap getFieldMap(Class cl) {
HashMap fieldMap = super.getFieldMap(cl);
Field valueField = null;
try {
valueField = cl.getSuperclass().getDeclaredField("value");
valueField.setAccessible(true);
} catch (Throwable t) {
log.log(Level.WARNING, "get value field failed", t);
valueField = getAbstractStringBuilderField(cl, "value");

Check warning on line 69 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L67-L69

Added lines #L67 - L69 were not covered by tests
if (valueField == null) {
log.log(Level.WARNING, "get value field failed");
return fieldMap;

Check warning on line 72 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L71-L72

Added lines #L71 - L72 were not covered by tests
}

fieldMap.put("value", new StringBuilderValueFieldDeserializer(valueField));
Field coderField = null;

Check warning on line 75 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L75

Added line #L75 was not covered by tests
if (fieldMap.containsKey("coder")) {
coderField = getAbstractStringBuilderField(cl, "coder");

Check warning on line 77 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L77

Added line #L77 was not covered by tests
}

fieldMap.put("value", new StringBuilderValueFieldDeserializer(valueField, coderField));
return fieldMap;

Check warning on line 81 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L80-L81

Added lines #L80 - L81 were not covered by tests
}

/**
* 获取 AbstractStingBuilder 类内的 field
* @param cl
* @param fieldName
* @return
*/
private Field getAbstractStringBuilderField(Class cl, String fieldName) {
Field field = null;

Check warning on line 91 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L91

Added line #L91 was not covered by tests
try {
field = cl.getSuperclass().getDeclaredField(fieldName);
field.setAccessible(true);
} catch (Throwable t) {
log.log(Level.WARNING, "get " + fieldName + " field failed", t);
return null;
}
return field;

Check warning on line 99 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L93-L99

Added lines #L93 - L99 were not covered by tests
}

/**
* 针对 value field 定制的 field deserializer
* 读取 value field 时, 根据传入数据进行读取, 读取到值后进行转换
*/
static class StringBuilderValueFieldDeserializer extends FieldDeserializer {
/**
* 这个 _field 会是 AbstractStringBuilder.value
*/
private final Field _field;

StringBuilderValueFieldDeserializer(Field field) {
_field = field;
/**
* _coderField 会是 AbstractStringBuilder.coder 字段
*/
private final Field _coderField;

StringBuilderValueFieldDeserializer(Field value, Field coder) {
_field = value;
_coderField = coder;
}

Check warning on line 120 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L117-L120

Added lines #L117 - L120 were not covered by tests

@Override
Expand All @@ -88,8 +132,7 @@ void deserialize(AbstractHessianInput in, Object obj) throws IOException {

// 理论上获取到的值有两种情况: String 或者 byte[]
if (value instanceof String) {
byte[] res = hessianString2Bytes((String) value);
_field.set(obj, res);
dealWithStringValue((String) value, obj);

Check warning on line 135 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L135

Added line #L135 was not covered by tests
} else if (value instanceof byte[]) {
_field.set(obj, value);

Check warning on line 137 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L137

Added line #L137 was not covered by tests
} else {
Expand All @@ -101,28 +144,20 @@ void deserialize(AbstractHessianInput in, Object obj) throws IOException {
}

Check warning on line 144 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L141-L144

Added lines #L141 - L144 were not covered by tests

/**
* 按照 hessian 序列化的处理方式将 String 还原为 byte[]
* {@link Hessian2Output#printString(java.lang.String, int, int)}
* @param str
* @return
* 如果读取到的是 String, 需要通过 String.coder 进行编码
*/
private byte[] hessianString2Bytes(String str) {
byte[] buffer = new byte[str.length()];
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);

if (ch < 0x80)
buffer[i] = (byte) (ch);
else if (ch < 0x800) {
buffer[i] = (byte) (0xc0 + ((ch >> 6) & 0x1f));
buffer[i] = (byte) (0x80 + (ch & 0x3f));
} else {
buffer[i] = (byte) (0xe0 + ((ch >> 12) & 0xf));
buffer[i] = (byte) (0x80 + ((ch >> 6) & 0x3f));
buffer[i] = (byte) (0x80 + (ch & 0x3f));
public void dealWithStringValue(String value, Object obj) {
try {
byte[] res = (byte[]) stringValueField.get(value);
_field.set(obj, res);

Check warning on line 152 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L151-L152

Added lines #L151 - L152 were not covered by tests

if (stringCoderField != null) {
byte coder = (byte) stringCoderField.getByte(value);
_coderField.set(obj, coder);

Check warning on line 156 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L155-L156

Added lines #L155 - L156 were not covered by tests
}
} catch (Throwable t) {

Check warning on line 158 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L158

Added line #L158 was not covered by tests

}
return buffer;
}

Check warning on line 161 in src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java#L160-L161

Added lines #L160 - L161 were not covered by tests
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.caucho.hessian.io.java17.lang;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;

/**
* <ol>
* <li>确保新老两种序列化方式内容一致(由于类实现有变动, 无法保证 jdk17 和 jdk8 序列化结果完全一致)</li>
* <li>确保 jdk8 下的序列化结果能被 jdk8 下的反序列化器正确解析</li>
* </ol>
*
*
* @author junyuan
* @version AbstractStringBuilderEncodeTest.java, v 0.1 2023年10月20日 10:07 junyuan Exp $
*/
public class AbstractStringBuilderEncodeTest {

private static SerializerFactory factory;
private static SerializerFactory previousVersionFactory;
private static ByteArrayOutputStream os;

@BeforeClass
public static void setUp() {
factory = new SerializerFactory();
previousVersionFactory = new StringBuilderJavaSerializeFactory();

os = new ByteArrayOutputStream();
}

private void compatibleTest(Object inputData, SerializerFactory newFactory, SerializerFactory oldFactory) {
byte[] resNew = new byte[0];
byte[] resOld = new byte[0];

try {
resNew = doEncodeWithStringBuilder(inputData, newFactory);
resOld = doEncodeWithStringBuilder(inputData, oldFactory);
} catch (Throwable e) {
e.printStackTrace();
Assert.fail();
}

boolean result = Arrays.equals(resNew, resOld);
Assert.assertTrue(result);
}

@Test
public void compatibleTest_short_no_warpper() {
StringBuilder sb = new StringBuilder();
sb.append("test");

compatibleTest(sb, factory, previousVersionFactory);
}

@Test
public void compatibleTest_long_no_warpper() {
StringBuilder sb = new StringBuilder();
sb.append("100000000020000000003000000000400000000050000000006000000000");

compatibleTest(sb, factory, previousVersionFactory);
}

/**
* short string with length lt 16
*/
@Test
public void compatibleTest_short_wrapper() {
StringBuilder sb = new StringBuilder();
sb.append("test");
StringBuilderWrapper sbw = new StringBuilderWrapper();
sbw.setStringBuilder(sb);
sbw.setStr(sb.toString());

compatibleTest(sbw, factory, previousVersionFactory);
}

/**
* long string with length gt 31
*/
@Test
public void compatibleTest_long_wrapper() {
StringBuilder sb = new StringBuilder();
sb.append("100000000020000000003000000000400000000050000000006000000000");
StringBuilderWrapper sbw = new StringBuilderWrapper();
sbw.setStringBuilder(sb);
sbw.setStr(sb.toString());

compatibleTest(sbw, factory, previousVersionFactory);
}

/**
* UTF16
*/
@Test
public void compatibleTest_wrapper_short_utf16() {
StringBuilder sb = new StringBuilder();
sb.append("test测试");
StringBuilderWrapper sbw = new StringBuilderWrapper();
sbw.setStringBuilder(sb);
sbw.setStr(sb.toString());

compatibleTest(sbw, factory, previousVersionFactory);
}

/**
* UTF16
*/
@Test
public void compatibleTest_wrapper_long_utf16() {
StringBuilder sb = new StringBuilder();
sb.append("aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeefffffffffftest测试");
StringBuilderWrapper sbw = new StringBuilderWrapper();
sbw.setStringBuilder(sb);
sbw.setStr(sb.toString());

compatibleTest(sbw, factory, previousVersionFactory);
}

protected byte[] doEncodeWithStringBuilder(Object data, SerializerFactory factory)
throws IOException {
os.reset();
Hessian2Output out = new Hessian2Output(os);
out.setSerializerFactory(factory);
out.writeObject(data);
out.flush();

return os.toByteArray();
}

/*
*打印byte[],以逗号分隔
*/
private static void printBytes(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
System.out.println("null");
return;
}

for (int i = 0; i < bytes.length; i++) {
System.out.print(bytes[i] + ",");
}
System.out.println();
}
}
Loading

0 comments on commit 5e2dfc2

Please sign in to comment.