java与C#用protobuf通信--java如何转换protobuf-net中的bcl.Decimal对象
公司内部有些C#服务使用proto-net,引入了bcl.proto中的bcl.Decimal、bcl.DateTime等。对于java的proto生成代码需要对bcl.Decimal、bcl.DateTime转换成本地支持的数据类型。
bcl.Decimal结构为32位int保存高位、64位long保存低位,signScale 保存符号(正负)及模数。转换过程如下:
bcl.Decimal --> BigDecimal
1. 将高32位的int以高位在前格式转换为4个长度的byte数组;
2. 将低64位的long以高位在前的格式转换为8个长度的byte数组;
3. 将两个数组组合为12长度的byte数组bigIntBytes,获取signScale的符号值sgin = (signScale & 1) == 1 ? -1 : 1; (参考bcl.Decimal 注释:the number of decimal digits (bits 1-16), and the sign (bit 0))
4. 根据bigIntBytes和sgin获取BigInteger对象bInt,new BigInteger(sgin,bigIntBytes);
5. 获取signScale的模数值scale = (signScale & 0b1111_1111_1111_1110) >> 1; (参考bcl.Decimal 注释:the number of decimal digits (bits 1-16), and the sign (bit 0))
6.根据bInt和scale获取BigDecimal对象,new BigDecimal(bint, scale);
BigDecimal --> bcl.Decimal
上述过程反之即可
代码如下:
/*JDK1.7*/
public class BclUtil { /** * */ private static final long TICKS_PER_MILLISECOND = 10000; private static final int SIGNSCALE_FLAG = 0b1111_1111_1111_1110; private BclUtil() { } /** * 转换 bcl.Decimal,返回BigDecimal<br> * 高位在前<br> * * @param bclDecimal * @return */ public final static BigDecimal bclDecimalToBigDecimal(Bcl.Decimal bclDecimal) { if (bclDecimal == null) { return BigDecimal.ZERO; } byte[] deBytes = new byte[12]; int2byte(deBytes, 0, bclDecimal.getHi()); long2byte(deBytes, 4, bclDecimal.getLo()); int signScale = bclDecimal.getSignScale(); BigInteger bint = new BigInteger(getSign(signScale), deBytes); return new BigDecimal(bint, getScale(signScale)); } /** * 转换BigDecimal,返回bcl.Decimal<br> * 高位在前<br> * * @param bigDecimal * @return */ public final static Bcl.Decimal bigDecimalToBclDecimal(BigDecimal bigDecimal) { if (bigDecimal == null) { return null; } Bcl.Decimal.Builder b = Bcl.Decimal.newBuilder(); // 获取BigInteger,必须是unscaledValue BigInteger bi = bigDecimal.unscaledValue(); byte[] deBytes = new byte[12]; byte[] bigIntegerBytes = bi.toByteArray(); System.arraycopy(bigIntegerBytes, 0, deBytes, 12 - bigIntegerBytes.length, bigIntegerBytes.length); // 取高4位int int hi = Eutil.bytes2int(deBytes, 0); b.setHi(hi); // 取低8位long long lo = Eutil.bytes2long(deBytes, 4); b.setLo(lo); int signScale = getSignScale(bi.signum(), bigDecimal.scale()); b.setSignScale(signScale); return b.build(); } private static int getSignScale(int signum, int scale) { // 从BigInteger获取取标志位放在bit 0/从BigDecimal获取取标志位放在bit 1-16 return getSignInSignScale(signum) | (scale << 1); } public final static Bcl.Decimal zeroOfBclDecimal() { Bcl.Decimal.Builder b = Bcl.Decimal.newBuilder(); b.setHi(0); b.setLo(0); b.setSignScale(0); return b.build(); } /** * 填充值符号<br> * * @param sign * @return */ public final static byte getSignInSignScale(int sign) { byte signTemp = 0; if (sign < 0) { signTemp = 1; } return signTemp; } /** * 获取值符号<br> * * @param signScale * @return */ public final static int getSign(int signScale) { // the number of decimal digits (bits 1-16), and the sign (bit 0) boolean isNegative = (signScale & 1) == 1; return isNegative ? -1 : 1; } /** * 获取模数 * * @param signScale * @return */ public final static int getScale(int signScale) { // the number of decimal digits (bits 1-16), and the sign (bit 0) return (signScale & SIGNSCALE_FLAG) >> 1; } /** * 获取int的byte数组,高位在前 * * @param dst * @param pos * @param src */ public final static void int2byte(byte[] dst, int pos, int src) { int2byte(dst, pos, src, true); } public final static byte[] int2byte(byte[] dst, int pos, int src, boolean big_endian) { if (big_endian) { dst[pos + 3] = (byte) ((src >>> 0) & 0xff); dst[pos + 2] = (byte) ((src >>> 8) & 0xff); dst[pos + 1] = (byte) ((src >>> 16) & 0xff); dst[pos + 0] = (byte) ((src >>> 24) & 0xff); } else { dst[pos + 0] = (byte) ((src >>> 0) & 0xff); dst[pos + 1] = (byte) ((src >>> 8) & 0xff); dst[pos + 2] = (byte) ((src >>> 16) & 0xff); dst[pos + 3] = (byte) ((src >>> 24) & 0xff); } return dst; } public final static byte[] int2unSignByte(byte[] dst, int pos, int src, boolean big_endian) { if (big_endian) { dst[pos + 3] = (byte) ((src >>> 0) & 0xff + 128); dst[pos + 2] = (byte) ((src >>> 8) & 0xff + 128); dst[pos + 1] = (byte) ((src >>> 16) & 0xff + 128); dst[pos + 0] = (byte) ((src >>> 24) & 0xff + 128); } else { dst[pos + 0] = (byte) ((src >>> 0) & 0xff + 128); dst[pos + 1] = (byte) ((src >>> 8) & 0xff + 128); dst[pos + 2] = (byte) ((src >>> 16) & 0xff + 128); dst[pos + 3] = (byte) ((src >>> 24) & 0xff + 128); } return dst; } /** * 获取long的byte数组,高位在前 * * @param dst * @param pos * @param src */ final static void long2byte(byte[] dst, int pos, long src) { long2byte(dst, pos, src, true); } final static void long2byte(byte[] dst, int pos, long src, boolean big_endian) { if (big_endian) { int2byte(dst, 4 + pos, (int) (src & 0xffffffff)); int2byte(dst, 0 + pos, (int) ((src >>> 32) & 0xffffffff)); } else { int2byte(dst, 0 + pos, (int) (src & 0xffffffff)); int2byte(dst, 4 + pos, (int) ((src >>> 32) & 0xffffffff)); } } }