|
22 | 22 | import java.io.Closeable; |
23 | 23 | import java.io.Flushable; |
24 | 24 | import java.io.IOException; |
| 25 | +import java.lang.reflect.Constructor; |
| 26 | +import java.lang.reflect.Field; |
| 27 | +import java.lang.reflect.InvocationTargetException; |
25 | 28 | import java.math.BigInteger; |
26 | 29 | import java.nio.ByteBuffer; |
27 | 30 | import java.nio.CharBuffer; |
|
130 | 133 | public class MessagePacker |
131 | 134 | implements Closeable, Flushable |
132 | 135 | { |
| 136 | + private static final boolean CORRUPTED_CHARSET_ENCODER; |
| 137 | + |
| 138 | + static { |
| 139 | + boolean corruptedCharsetEncoder = false; |
| 140 | + try { |
| 141 | + Class<?> klass = Class.forName("android.os.Build$VERSION"); |
| 142 | + Constructor<?> constructor = klass.getConstructor(); |
| 143 | + Object version = constructor.newInstance(); |
| 144 | + Field sdkIntField = klass.getField("SDK_INT"); |
| 145 | + int sdkInt = sdkIntField.getInt(version); |
| 146 | + // Android 4.x has a bug in CharsetEncoder where offset calculation is wrong. |
| 147 | + // See |
| 148 | + // - https://github.com/msgpack/msgpack-java/issues/405 |
| 149 | + // - https://github.com/msgpack/msgpack-java/issues/406 |
| 150 | + // Android 5 and later and 3.x don't have this bug. |
| 151 | + if (sdkInt >= 14 && sdkInt < 21) { |
| 152 | + corruptedCharsetEncoder = true; |
| 153 | + } |
| 154 | + } |
| 155 | + catch (ClassNotFoundException e) { |
| 156 | + // This platform isn't Android |
| 157 | + } |
| 158 | + catch (NoSuchMethodException e) { |
| 159 | + e.printStackTrace(); |
| 160 | + } |
| 161 | + catch (IllegalAccessException e) { |
| 162 | + e.printStackTrace(); |
| 163 | + } |
| 164 | + catch (InstantiationException e) { |
| 165 | + e.printStackTrace(); |
| 166 | + } |
| 167 | + catch (InvocationTargetException e) { |
| 168 | + e.printStackTrace(); |
| 169 | + } |
| 170 | + catch (NoSuchFieldException e) { |
| 171 | + e.printStackTrace(); |
| 172 | + } |
| 173 | + CORRUPTED_CHARSET_ENCODER = corruptedCharsetEncoder; |
| 174 | + } |
| 175 | + |
133 | 176 | private final int smallStringOptimizationThreshold; |
134 | 177 |
|
135 | 178 | private final int bufferFlushThreshold; |
@@ -675,8 +718,9 @@ public MessagePacker packString(String s) |
675 | 718 | packRawStringHeader(0); |
676 | 719 | return this; |
677 | 720 | } |
678 | | - else if (s.length() < smallStringOptimizationThreshold) { |
679 | | - // Using String.getBytes is generally faster for small strings |
| 721 | + else if (CORRUPTED_CHARSET_ENCODER || s.length() < smallStringOptimizationThreshold) { |
| 722 | + // Using String.getBytes is generally faster for small strings. |
| 723 | + // Also, when running on a platform that has a corrupted CharsetEncoder (i.e. Android 4.x), avoid using it. |
680 | 724 | packStringWithGetBytes(s); |
681 | 725 | return this; |
682 | 726 | } |
|
0 commit comments