hexUtils.dart 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import 'dart:convert' show utf8;
  2. import 'dart:typed_data';
  3. import 'package:convert/convert.dart' show hex;
  4. import "package:pointycastle/digests/keccak.dart";
  5. bool isHexPrefixed(String str) {
  6. ArgumentError.checkNotNull(str);
  7. return str.startsWith('0x');
  8. }
  9. /// Is the string a hex string.
  10. bool isHexString(String value, {int length = 0}) {
  11. ArgumentError.checkNotNull(value);
  12. // if (!RegExp('^0x[0-9A-Fa-f]*\$').hasMatch(value)) return false;
  13. if (!RegExp(r'\b(?:0[xX])?[0-9a-fA-F]+\b').hasMatch(value)) return false;
  14. if (length > 0 && value.length != 2 + 2 * length) return false;
  15. return true;
  16. }
  17. String stripHexPrefix(String hexString) {
  18. ArgumentError.checkNotNull(hexString);
  19. if (hexString.startsWith("0x") || hexString.startsWith("0X")) {
  20. return hexString.substring(2);
  21. }
  22. return hexString;
  23. }
  24. String addHexPrefix(String hexString) {
  25. ArgumentError.checkNotNull(hexString);
  26. if (!hexString.startsWith("0x") && !hexString.startsWith("0X")) {
  27. return '0x$hexString';
  28. }
  29. return hexString;
  30. }
  31. /// Pads a [String] to have an even length
  32. String padToEven(String value) {
  33. ArgumentError.checkNotNull(value);
  34. var a = '$value';
  35. if (a.length % 2 == 1) a = '0${a}';
  36. return a;
  37. }
  38. /// Converts a [int] into a hex [String]
  39. String intToHex(i) {
  40. ArgumentError.checkNotNull(i);
  41. return '0x${i.toRadixString(16)}';
  42. }
  43. /// Converts an [int] or [BigInt] to a [Uint8List]
  44. Uint8List intToBytes(i) {
  45. return Uint8List.fromList(i == null || i == 0 || i == BigInt.zero ? [] : hex.decode(padToEven(intToHex(i).substring(2))));
  46. }
  47. Uint8List stringToBytes(String str) {
  48. ArgumentError.checkNotNull(str);
  49. if (str.isEmpty) {
  50. return Uint8List(0);
  51. }
  52. // Use UTF-8 encoding to convert string to bytes
  53. return Uint8List.fromList(utf8.encode(str));
  54. }
  55. /// Get the binary size of a string
  56. int getBinarySize(String str) {
  57. ArgumentError.checkNotNull(str);
  58. return utf8.encode(str).length;
  59. }
  60. /// Returns TRUE if the first specified array contains all elements from the second one. FALSE otherwise.
  61. bool arrayContainsArray(List superset, List subset, {bool some = false}) {
  62. ArgumentError.checkNotNull(superset);
  63. ArgumentError.checkNotNull(subset);
  64. if (some) return Set.from(superset).intersection(Set.from(subset)).length > 0;
  65. return Set.from(superset).containsAll(subset);
  66. }
  67. /// Should be called to get utf8 from it's hex representation
  68. String toUtf8(String hexString) {
  69. ArgumentError.checkNotNull(hexString);
  70. List<int> bufferValue = hex.decode(padToEven(stripHexPrefix(hexString).replaceAll(RegExp('^0+|0+\$'), '')));
  71. return utf8.decode(bufferValue);
  72. }
  73. /// Should be called to get ascii from it's hex representation
  74. String toAscii(String hexString) {
  75. ArgumentError.checkNotNull(hexString);
  76. var start = hexString.startsWith(RegExp('^0x')) ? 2 : 0;
  77. return String.fromCharCodes(hex.decode(hexString.substring(start)));
  78. }
  79. /// Should be called to get hex representation (prefixed by 0x) of utf8 string
  80. String fromUtf8(String stringValue) {
  81. ArgumentError.checkNotNull(stringValue);
  82. var stringBuffer = utf8.encode(stringValue);
  83. return "0x${padToEven(hex.encode(stringBuffer)).replaceAll(RegExp('^0+|0+\$'), '')}";
  84. }
  85. /// Should be called to get hex representation (prefixed by 0x) of ascii string
  86. String fromAscii(String stringValue) {
  87. ArgumentError.checkNotNull(stringValue);
  88. var hexString = '';
  89. for (var i = 0; i < stringValue.length; i++) {
  90. var code = stringValue.codeUnitAt(i);
  91. var n = hex.encode([code]);
  92. hexString += n.length < 2 ? '0${n}' : n;
  93. }
  94. return '0x$hexString';
  95. }
  96. Uint8List hexToBytes(String hexStr) {
  97. final bytes = hex.decode(stripHexPrefix(hexStr));
  98. if (bytes is Uint8List) return bytes;
  99. return Uint8List.fromList(bytes);
  100. }
  101. String bytesToHex(List<int> bytes,
  102. {bool include0x = false,
  103. int? forcePadLength,
  104. bool padToEvenLength = false}) {
  105. var encoded = hex.encode(bytes);
  106. if (forcePadLength != null) {
  107. assert(forcePadLength >= encoded.length);
  108. final padding = forcePadLength - encoded.length;
  109. encoded = ('0' * padding) + encoded;
  110. }
  111. if (padToEvenLength && encoded.length % 2 != 0) encoded = '0$encoded';
  112. return (include0x ? '0x' : '') + encoded;
  113. }
  114. // ------------ by myself -----------
  115. String hex2ascii(String hexString) {
  116. try {
  117. if (isHexString(hexString)) {
  118. hexString = stripHexPrefix(hexString);
  119. List<String> splitted = [];
  120. for (int i = 0; i < hexString.length; i = i + 2) {
  121. splitted.add(hexString.substring(i, i + 2));
  122. }
  123. String ascii = List.generate(splitted.length,
  124. (i) => String.fromCharCode(int.parse(splitted[i], radix: 16))).join();
  125. return ascii;
  126. } else {
  127. return '';
  128. }
  129. } catch (error) {
  130. return '';
  131. }
  132. }
  133. String ascii2hex(String asciiString) {
  134. try {
  135. if (asciiString.isNotEmpty) {
  136. String hexStr = '';
  137. for (int i = 0; i < asciiString.length; ++i) {
  138. hexStr = hexStr + asciiString.codeUnitAt(i).toRadixString(16);
  139. }
  140. return hexStr;
  141. } else {
  142. return '';
  143. }
  144. } catch (error) {
  145. return '';
  146. }
  147. }
  148. String hex2dec(String hexString) {
  149. try {
  150. if (isHexString(hexString)) {
  151. BigInt? bgInt = BigInt.tryParse(stripHexPrefix(hexString), radix:16);
  152. if (bgInt != null) {
  153. return bgInt.toRadixString(10);
  154. }
  155. }
  156. // else
  157. return '';
  158. } catch (error) {
  159. return '';
  160. }
  161. }
  162. String dec2hex(String decString) {
  163. try {
  164. if (decString.isNotEmpty) {
  165. BigInt? bgInt = BigInt.tryParse(decString, radix:10);
  166. if (bgInt != null) {
  167. return addHexPrefix(bgInt.toRadixString(16));
  168. }
  169. }
  170. // else
  171. return '';
  172. } catch (error) {
  173. return '';
  174. }
  175. }
  176. /// ----- to Hash -----
  177. /// 注意:Hash256 和 Keccak256 其实不一样,
  178. /// 但由于最早接触 Ethereum 相关代码,其中 Hash 使用的是 Keccak256,
  179. /// 造成App其他地方的 Hash 也都沿用了 Keccak256。
  180. /// 历史原因,不做修改。[2023-12-15 James.Zhang]
  181. String toHash(String anyString) {
  182. return toKeccak256(anyString);
  183. }
  184. String toKeccak256(String anyString, [int bitLength = 256]) {
  185. var keccak = KeccakDigest(bitLength);
  186. keccak.update(
  187. Uint8List.fromList(anyString.codeUnits),
  188. 0,
  189. anyString.codeUnits.length,
  190. );
  191. var out = Uint8List(bitLength ~/ 8);
  192. keccak.doFinal(out, 0);
  193. return _uint8ListToHex(out);
  194. }
  195. /// Character `0`.
  196. const int _zero = 0x30;
  197. /// Character `a`.
  198. const int _a = 0x61;
  199. String _uint8ListToHex(List<int> bytes) {
  200. final buffer = Uint8List(bytes.length * 2);
  201. int bufferIndex = 0;
  202. for (var i = 0; i < bytes.length; i++) {
  203. var byte = bytes[i];
  204. // The bitwise arithmetic here is equivalent to `byte ~/ 16` and `byte % 16`
  205. // for valid byte values, but is easier for dart2js to optimize given that
  206. // it can't prove that [byte] will always be positive.
  207. buffer[bufferIndex++] = _codeUnitForDigit((byte & 0xF0) >> 4);
  208. buffer[bufferIndex++] = _codeUnitForDigit(byte & 0x0F);
  209. }
  210. return String.fromCharCodes(buffer);
  211. }
  212. /// Returns the ASCII/Unicode code unit corresponding to the hexadecimal digit
  213. /// [digit].
  214. int _codeUnitForDigit(int digit) =>
  215. digit < 10 ? digit + _zero : digit + _a - 10;