deployed 2.0.5.RELEASE,修复集合类型长度域BUG
parent
b7a938ff4a
commit
44050e72ce
2
pom.xml
2
pom.xml
|
@ -3,7 +3,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>io.github.yezhihao</groupId>
|
<groupId>io.github.yezhihao</groupId>
|
||||||
<artifactId>protostar</artifactId>
|
<artifactId>protostar</artifactId>
|
||||||
<version>2.0.4.RELEASE</version>
|
<version>2.0.5.RELEASE</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>Protostar</name>
|
<name>Protostar</name>
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
package io.github.yezhihao.protostar;
|
package io.github.yezhihao.protostar;
|
||||||
|
|
||||||
import io.github.yezhihao.protostar.annotation.Field;
|
import io.github.yezhihao.protostar.annotation.Field;
|
||||||
import io.github.yezhihao.protostar.field.BasicField;
|
import io.github.yezhihao.protostar.field.*;
|
||||||
import io.github.yezhihao.protostar.field.DynamicLengthField;
|
|
||||||
import io.github.yezhihao.protostar.field.FixedField;
|
|
||||||
import io.github.yezhihao.protostar.field.FixedLengthField;
|
|
||||||
import io.github.yezhihao.protostar.schema.*;
|
import io.github.yezhihao.protostar.schema.*;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -65,7 +62,7 @@ public abstract class FieldFactory {
|
||||||
fieldSchema = ConvertSchema.getInstance(field.converter());
|
fieldSchema = ConvertSchema.getInstance(field.converter());
|
||||||
break;
|
break;
|
||||||
case LIST:
|
case LIST:
|
||||||
fieldSchema = CollectionSchema.getInstance(schema, field.lengthSize());
|
fieldSchema = CollectionSchema.getInstance(schema);
|
||||||
break;
|
break;
|
||||||
case MAP:
|
case MAP:
|
||||||
fieldSchema = ConvertSchema.getInstance(field.converter());
|
fieldSchema = ConvertSchema.getInstance(field.converter());
|
||||||
|
@ -78,6 +75,9 @@ public abstract class FieldFactory {
|
||||||
BasicField result;
|
BasicField result;
|
||||||
if (EXPLAIN) {
|
if (EXPLAIN) {
|
||||||
if (field.lengthSize() > 0) {
|
if (field.lengthSize() > 0) {
|
||||||
|
if (fieldSchema instanceof CollectionSchema)
|
||||||
|
result = new DynamicTotalField.Logger(field, f, fieldSchema);
|
||||||
|
else
|
||||||
result = new DynamicLengthField.Logger(field, f, fieldSchema);
|
result = new DynamicLengthField.Logger(field, f, fieldSchema);
|
||||||
} else if (field.length() > 0) {
|
} else if (field.length() > 0) {
|
||||||
result = new FixedLengthField.Logger(field, f, fieldSchema);
|
result = new FixedLengthField.Logger(field, f, fieldSchema);
|
||||||
|
@ -85,7 +85,10 @@ public abstract class FieldFactory {
|
||||||
result = new FixedField.Logger(field, f, fieldSchema);
|
result = new FixedField.Logger(field, f, fieldSchema);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (field.lengthSize() > 0 && !(fieldSchema instanceof CollectionSchema)) {
|
if (field.lengthSize() > 0) {
|
||||||
|
if (fieldSchema instanceof CollectionSchema)
|
||||||
|
result = new DynamicTotalField(field, f, fieldSchema);
|
||||||
|
else
|
||||||
result = new DynamicLengthField(field, f, fieldSchema);
|
result = new DynamicLengthField(field, f, fieldSchema);
|
||||||
} else if (field.length() > 0) {
|
} else if (field.length() > 0) {
|
||||||
result = new FixedLengthField(field, f, fieldSchema);
|
result = new FixedLengthField(field, f, fieldSchema);
|
||||||
|
|
|
@ -70,10 +70,10 @@ public abstract class MapConverter<K, V> extends PrepareLoadStrategy implements
|
||||||
if (schema != null) {
|
if (schema != null) {
|
||||||
int lengthSize = valueSize();
|
int lengthSize = valueSize();
|
||||||
int begin = output.writerIndex();
|
int begin = output.writerIndex();
|
||||||
output.writeBytes(ByteBufUtils.BLOCKS[lengthSize]);
|
ByteBufUtils.writeInt(output, lengthSize, 0);
|
||||||
schema.writeTo(output, value);
|
schema.writeTo(output, value);
|
||||||
int length = output.writerIndex() - begin - lengthSize;
|
int length = output.writerIndex() - begin - lengthSize;
|
||||||
ByteBufUtils.setInt(output, lengthSize, begin, length);
|
ByteBufUtils.setInt(output, begin, lengthSize, length);
|
||||||
} else {
|
} else {
|
||||||
log.warn("未注册的信息:ID[{}], VALUE[{}]", key, value);
|
log.warn("未注册的信息:ID[{}], VALUE[{}]", key, value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,9 @@ public abstract class BasicField<T> implements Comparable<BasicField<T>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean readFrom(ByteBuf input, Object message) throws Exception;
|
public abstract boolean readFrom(ByteBuf input, T message) throws Exception;
|
||||||
|
|
||||||
public abstract void writeTo(ByteBuf output, Object message) throws Exception;
|
public abstract void writeTo(ByteBuf output, T message) throws Exception;
|
||||||
|
|
||||||
public void println(int index, String desc, String hex, Object value) {
|
public void println(int index, String desc, String hex, Object value) {
|
||||||
if (value != null)
|
if (value != null)
|
||||||
|
|
|
@ -13,11 +13,11 @@ import io.netty.buffer.ByteBufUtil;
|
||||||
*/
|
*/
|
||||||
public class DynamicLengthField<T> extends BasicField<T> {
|
public class DynamicLengthField<T> extends BasicField<T> {
|
||||||
|
|
||||||
protected final Schema<T> schema;
|
protected final Schema schema;
|
||||||
|
|
||||||
protected final int lengthSize;
|
protected final int lengthSize;
|
||||||
|
|
||||||
public DynamicLengthField(Field field, java.lang.reflect.Field f, Schema<T> schema) {
|
public DynamicLengthField(Field field, java.lang.reflect.Field f, Schema schema) {
|
||||||
super(field, f);
|
super(field, f);
|
||||||
this.schema = schema;
|
this.schema = schema;
|
||||||
this.lengthSize = field.lengthSize();
|
this.lengthSize = field.lengthSize();
|
||||||
|
@ -36,10 +36,10 @@ public class DynamicLengthField<T> extends BasicField<T> {
|
||||||
Object value = f.get(message);
|
Object value = f.get(message);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
int begin = output.writerIndex();
|
int begin = output.writerIndex();
|
||||||
output.writeBytes(ByteBufUtils.BLOCKS[lengthSize]);
|
ByteBufUtils.writeInt(output, lengthSize, 0);
|
||||||
schema.writeTo(output, (T) value);
|
schema.writeTo(output, value);
|
||||||
int length = output.writerIndex() - begin - lengthSize;
|
int length = output.writerIndex() - begin - lengthSize;
|
||||||
ByteBufUtils.setInt(output, lengthSize, begin, length);
|
ByteBufUtils.setInt(output, begin, lengthSize, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,10 +78,10 @@ public class DynamicLengthField<T> extends BasicField<T> {
|
||||||
Object value = f.get(message);
|
Object value = f.get(message);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
int begin = output.writerIndex();
|
int begin = output.writerIndex();
|
||||||
output.writeBytes(ByteBufUtils.BLOCKS[lengthSize]);
|
ByteBufUtils.writeInt(output, lengthSize, 0);
|
||||||
schema.writeTo(output, (T) value);
|
schema.writeTo(output, value);
|
||||||
int length = output.writerIndex() - begin - lengthSize;
|
int length = output.writerIndex() - begin - lengthSize;
|
||||||
ByteBufUtils.setInt(output, lengthSize, begin, length);
|
ByteBufUtils.setInt(output, begin, lengthSize, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
int after = output.writerIndex();
|
int after = output.writerIndex();
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package io.github.yezhihao.protostar.field;
|
||||||
|
|
||||||
|
import io.github.yezhihao.protostar.Schema;
|
||||||
|
import io.github.yezhihao.protostar.annotation.Field;
|
||||||
|
import io.github.yezhihao.protostar.util.ByteBufUtils;
|
||||||
|
import io.github.yezhihao.protostar.util.StrUtils;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态长度的字段
|
||||||
|
* @author yezhihao
|
||||||
|
* home https://gitee.com/yezhihao/jt808-server
|
||||||
|
*/
|
||||||
|
public class DynamicTotalField<T> extends BasicField<T> {
|
||||||
|
|
||||||
|
protected final Schema<Collection<?>> schema;
|
||||||
|
|
||||||
|
protected final int totalSize;
|
||||||
|
|
||||||
|
public DynamicTotalField(Field field, java.lang.reflect.Field f, Schema schema) {
|
||||||
|
super(field, f);
|
||||||
|
this.schema = schema;
|
||||||
|
this.totalSize = field.lengthSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean readFrom(ByteBuf input, Object message) throws Exception {
|
||||||
|
Object value = schema.readFrom(input, totalSize);
|
||||||
|
f.set(message, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo(ByteBuf output, Object message) throws Exception {
|
||||||
|
Collection value = (Collection) f.get(message);
|
||||||
|
if (value != null)
|
||||||
|
schema.writeTo(output, totalSize, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(BasicField<T> that) {
|
||||||
|
int r = Integer.compare(this.index, that.index);
|
||||||
|
if (r == 0)
|
||||||
|
r = (that instanceof DynamicTotalField) ? 1 : -1;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Logger<T> extends DynamicTotalField<T> {
|
||||||
|
|
||||||
|
public Logger(Field field, java.lang.reflect.Field f, Schema<T> schema) {
|
||||||
|
super(field, f, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean readFrom(ByteBuf input, Object message) throws Exception {
|
||||||
|
int total = ByteBufUtils.getInt(input, input.readerIndex(), totalSize);
|
||||||
|
String hex = StrUtils.leftPad(Integer.toHexString(total), totalSize << 1, '0');
|
||||||
|
println(this.index, this.field.desc() + "总数", hex, total);
|
||||||
|
|
||||||
|
Collection value = schema.readFrom(input, totalSize);
|
||||||
|
f.set(message, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo(ByteBuf output, Object message) throws Exception {
|
||||||
|
Collection value = (Collection) f.get(message);
|
||||||
|
if (value != null) {
|
||||||
|
|
||||||
|
int total = value.size();
|
||||||
|
String hex = StrUtils.leftPad(Integer.toHexString(total), totalSize << 1, '0');
|
||||||
|
println(this.index, this.field.desc() + "总数", hex, total);
|
||||||
|
|
||||||
|
schema.writeTo(output, totalSize, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,9 +12,9 @@ import io.netty.buffer.ByteBufUtil;
|
||||||
*/
|
*/
|
||||||
public class FixedField<T> extends BasicField<T> {
|
public class FixedField<T> extends BasicField<T> {
|
||||||
|
|
||||||
protected final Schema<T> schema;
|
protected final Schema schema;
|
||||||
|
|
||||||
public FixedField(Field field, java.lang.reflect.Field f, Schema<T> schema) {
|
public FixedField(Field field, java.lang.reflect.Field f, Schema schema) {
|
||||||
super(field, f);
|
super(field, f);
|
||||||
this.schema = schema;
|
this.schema = schema;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ public class FixedField<T> extends BasicField<T> {
|
||||||
public void writeTo(ByteBuf output, Object message) throws Exception {
|
public void writeTo(ByteBuf output, Object message) throws Exception {
|
||||||
Object value = f.get(message);
|
Object value = f.get(message);
|
||||||
if (value != null)
|
if (value != null)
|
||||||
schema.writeTo(output, (T) value);
|
schema.writeTo(output, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Logger<T> extends FixedField<T> {
|
public static class Logger<T> extends FixedField<T> {
|
||||||
|
@ -54,7 +54,7 @@ public class FixedField<T> extends BasicField<T> {
|
||||||
|
|
||||||
Object value = f.get(message);
|
Object value = f.get(message);
|
||||||
if (value != null)
|
if (value != null)
|
||||||
schema.writeTo(output, (T) value);
|
schema.writeTo(output, value);
|
||||||
|
|
||||||
int after = output.writerIndex();
|
int after = output.writerIndex();
|
||||||
String hex = ByteBufUtil.hexDump(output.slice(before, after - before));
|
String hex = ByteBufUtil.hexDump(output.slice(before, after - before));
|
||||||
|
|
|
@ -12,7 +12,7 @@ import io.netty.buffer.ByteBufUtil;
|
||||||
*/
|
*/
|
||||||
public class FixedLengthField<T> extends BasicField<T> {
|
public class FixedLengthField<T> extends BasicField<T> {
|
||||||
|
|
||||||
protected final Schema<T> schema;
|
protected final Schema schema;
|
||||||
|
|
||||||
public FixedLengthField(Field field, java.lang.reflect.Field f, Schema<T> schema) {
|
public FixedLengthField(Field field, java.lang.reflect.Field f, Schema<T> schema) {
|
||||||
super(field, f);
|
super(field, f);
|
||||||
|
@ -28,7 +28,7 @@ public class FixedLengthField<T> extends BasicField<T> {
|
||||||
public void writeTo(ByteBuf output, Object message) throws Exception {
|
public void writeTo(ByteBuf output, Object message) throws Exception {
|
||||||
Object value = f.get(message);
|
Object value = f.get(message);
|
||||||
if (value != null)
|
if (value != null)
|
||||||
schema.writeTo(output, length, (T) value);
|
schema.writeTo(output, length, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Logger<T> extends FixedLengthField<T> {
|
public static class Logger<T> extends FixedLengthField<T> {
|
||||||
|
@ -54,7 +54,7 @@ public class FixedLengthField<T> extends BasicField<T> {
|
||||||
|
|
||||||
Object value = f.get(message);
|
Object value = f.get(message);
|
||||||
if (value != null)
|
if (value != null)
|
||||||
schema.writeTo(output, length, (T) value);
|
schema.writeTo(output, length, value);
|
||||||
|
|
||||||
int after = output.writerIndex();
|
int after = output.writerIndex();
|
||||||
String hex = ByteBufUtil.hexDump(output.slice(before, after - before));
|
String hex = ByteBufUtil.hexDump(output.slice(before, after - before));
|
||||||
|
|
|
@ -6,77 +6,64 @@ import io.github.yezhihao.protostar.util.Cache;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
|
|
||||||
public class CollectionSchema<T> implements Schema<List<T>> {
|
public class CollectionSchema<T> implements Schema<Collection<T>> {
|
||||||
|
|
||||||
private static final Cache<String, CollectionSchema> CACHE = new Cache<>();
|
private static final Cache<Schema, CollectionSchema> CACHE = new Cache<>();
|
||||||
|
|
||||||
public static CollectionSchema getInstance(Schema schema, int lengthSize) {
|
public static CollectionSchema getInstance(Schema schema) {
|
||||||
return CACHE.get(schema.hashCode() + "_" + lengthSize, key -> new CollectionSchema(schema, lengthSize));
|
return CACHE.get(schema, key -> new CollectionSchema(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Schema<T> schema;
|
private final Schema<T> schema;
|
||||||
|
|
||||||
private final int lengthSize;
|
private CollectionSchema(Schema<T> schema) {
|
||||||
|
|
||||||
private CollectionSchema(Schema<T> schema, int lengthSize) {
|
|
||||||
this.schema = schema;
|
this.schema = schema;
|
||||||
this.lengthSize = lengthSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<T> readFrom(ByteBuf input) {
|
public Collection<T> readFrom(ByteBuf input) {
|
||||||
if (!input.isReadable())
|
if (!input.isReadable())
|
||||||
return null;
|
return null;
|
||||||
List<T> list;
|
Collection<T> list = new ArrayList<>(2);
|
||||||
if (lengthSize > 0) {
|
|
||||||
int length = ByteBufUtils.readInt(input, lengthSize);
|
|
||||||
list = new ArrayList<>(length);
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
T obj = schema.readFrom(input);
|
|
||||||
if (obj == null) break;
|
|
||||||
list.add(obj);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
list = new ArrayList<>(2);
|
|
||||||
do {
|
do {
|
||||||
T obj = schema.readFrom(input);
|
T obj = schema.readFrom(input);
|
||||||
if (obj == null) break;
|
if (obj == null) break;
|
||||||
list.add(obj);
|
list.add(obj);
|
||||||
} while (input.isReadable());
|
} while (input.isReadable());
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<T> readFrom(ByteBuf input, int totalSize) {
|
||||||
|
int total = ByteBufUtils.readInt(input, totalSize);
|
||||||
|
Collection<T> list = new ArrayList<>(total);
|
||||||
|
for (int i = 0; i < total; i++) {
|
||||||
|
T obj = schema.readFrom(input);
|
||||||
|
list.add(obj);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<T> readFrom(ByteBuf input, int length) {
|
public void writeTo(ByteBuf output, Collection<T> list) {
|
||||||
int writerIndex = input.writerIndex();
|
|
||||||
input.writerIndex(input.readerIndex() + length);
|
|
||||||
List<T> result = this.readFrom(input);
|
|
||||||
input.writerIndex(writerIndex);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(ByteBuf output, List<T> list) {
|
|
||||||
if (list == null || list.isEmpty())
|
if (list == null || list.isEmpty())
|
||||||
return;
|
return;
|
||||||
if (lengthSize > 0)
|
|
||||||
ByteBufUtils.writeInt(output, lengthSize, list.size());
|
|
||||||
for (T obj : list) {
|
for (T obj : list) {
|
||||||
schema.writeTo(output, obj);
|
schema.writeTo(output, obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(ByteBuf output, int length, List<T> list) {
|
public void writeTo(ByteBuf output, int totalSize, Collection<T> list) {
|
||||||
if (list == null || list.isEmpty())
|
if (list == null || list.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
ByteBufUtils.writeInt(output, totalSize, list.size());
|
||||||
for (T obj : list) {
|
for (T obj : list) {
|
||||||
schema.writeTo(output, length, obj);
|
schema.writeTo(output, totalSize, obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,13 +9,6 @@ import io.netty.buffer.ByteBuf;
|
||||||
*/
|
*/
|
||||||
public class ByteBufUtils {
|
public class ByteBufUtils {
|
||||||
|
|
||||||
/** 长度域占位数据块 */
|
|
||||||
public static final byte[][] BLOCKS = new byte[][]{
|
|
||||||
new byte[0],
|
|
||||||
new byte[1], new byte[2],
|
|
||||||
new byte[3], new byte[4]};
|
|
||||||
|
|
||||||
|
|
||||||
public static int readInt(ByteBuf input, int length) {
|
public static int readInt(ByteBuf input, int length) {
|
||||||
int value;
|
int value;
|
||||||
switch (length) {
|
switch (length) {
|
||||||
|
@ -56,20 +49,20 @@ public class ByteBufUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getInt(ByteBuf output, int length, int index) {
|
public static int getInt(ByteBuf input, int index, int length) {
|
||||||
int value;
|
int value;
|
||||||
switch (length) {
|
switch (length) {
|
||||||
case 1:
|
case 1:
|
||||||
value = output.getUnsignedByte(index);
|
value = input.getUnsignedByte(index);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
value = output.getUnsignedShort(index);
|
value = input.getUnsignedShort(index);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
value = output.getUnsignedMedium(index);
|
value = input.getUnsignedMedium(index);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
value = output.getInt(index);
|
value = input.getInt(index);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("unsupported length: " + length + " (expected: 1, 2, 3, 4)");
|
throw new RuntimeException("unsupported length: " + length + " (expected: 1, 2, 3, 4)");
|
||||||
|
@ -77,7 +70,7 @@ public class ByteBufUtils {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setInt(ByteBuf output, int length, int index, int value) {
|
public static void setInt(ByteBuf output, int index, int length, int value) {
|
||||||
switch (length) {
|
switch (length) {
|
||||||
case 1:
|
case 1:
|
||||||
output.setByte(index, value);
|
output.setByte(index, value);
|
||||||
|
|
|
@ -90,4 +90,17 @@ public class StrUtils {
|
||||||
return false;
|
return false;
|
||||||
return componentType.isArray();
|
return componentType.isArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String leftPad(String str, int size, char ch) {
|
||||||
|
int length = str.length();
|
||||||
|
int pads = size - length;
|
||||||
|
if (pads > 0) {
|
||||||
|
char[] result = new char[size];
|
||||||
|
str.getChars(0, length, result, pads);
|
||||||
|
while (pads > 0)
|
||||||
|
result[--pads] = ch;
|
||||||
|
return new String(result);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue