data.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /**
  2. * Some data helpers.
  3. *
  4. *
  5. * @_subsection api/utils:Data Helpers [about-data]
  6. */
  7. import { assert, assertArgument } from "./errors.js";
  8. function _getBytes(value, name, copy) {
  9. if (value instanceof Uint8Array) {
  10. if (copy) {
  11. return new Uint8Array(value);
  12. }
  13. return value;
  14. }
  15. if (typeof (value) === "string" && value.match(/^0x(?:[0-9a-f][0-9a-f])*$/i)) {
  16. const result = new Uint8Array((value.length - 2) / 2);
  17. let offset = 2;
  18. for (let i = 0; i < result.length; i++) {
  19. result[i] = parseInt(value.substring(offset, offset + 2), 16);
  20. offset += 2;
  21. }
  22. return result;
  23. }
  24. assertArgument(false, "invalid BytesLike value", name || "value", value);
  25. }
  26. /**
  27. * Get a typed Uint8Array for %%value%%. If already a Uint8Array
  28. * the original %%value%% is returned; if a copy is required use
  29. * [[getBytesCopy]].
  30. *
  31. * @see: getBytesCopy
  32. */
  33. export function getBytes(value, name) {
  34. return _getBytes(value, name, false);
  35. }
  36. /**
  37. * Get a typed Uint8Array for %%value%%, creating a copy if necessary
  38. * to prevent any modifications of the returned value from being
  39. * reflected elsewhere.
  40. *
  41. * @see: getBytes
  42. */
  43. export function getBytesCopy(value, name) {
  44. return _getBytes(value, name, true);
  45. }
  46. /**
  47. * Returns true if %%value%% is a valid [[HexString]].
  48. *
  49. * If %%length%% is ``true`` or a //number//, it also checks that
  50. * %%value%% is a valid [[DataHexString]] of %%length%% (if a //number//)
  51. * bytes of data (e.g. ``0x1234`` is 2 bytes).
  52. */
  53. export function isHexString(value, length) {
  54. if (typeof (value) !== "string" || !value.match(/^0x[0-9A-Fa-f]*$/)) {
  55. return false;
  56. }
  57. if (typeof (length) === "number" && value.length !== 2 + 2 * length) {
  58. return false;
  59. }
  60. if (length === true && (value.length % 2) !== 0) {
  61. return false;
  62. }
  63. return true;
  64. }
  65. /**
  66. * Returns true if %%value%% is a valid representation of arbitrary
  67. * data (i.e. a valid [[DataHexString]] or a Uint8Array).
  68. */
  69. export function isBytesLike(value) {
  70. return (isHexString(value, true) || (value instanceof Uint8Array));
  71. }
  72. const HexCharacters = "0123456789abcdef";
  73. /**
  74. * Returns a [[DataHexString]] representation of %%data%%.
  75. */
  76. export function hexlify(data) {
  77. const bytes = getBytes(data);
  78. let result = "0x";
  79. for (let i = 0; i < bytes.length; i++) {
  80. const v = bytes[i];
  81. result += HexCharacters[(v & 0xf0) >> 4] + HexCharacters[v & 0x0f];
  82. }
  83. return result;
  84. }
  85. /**
  86. * Returns a [[DataHexString]] by concatenating all values
  87. * within %%data%%.
  88. */
  89. export function concat(datas) {
  90. return "0x" + datas.map((d) => hexlify(d).substring(2)).join("");
  91. }
  92. /**
  93. * Returns the length of %%data%%, in bytes.
  94. */
  95. export function dataLength(data) {
  96. if (isHexString(data, true)) {
  97. return (data.length - 2) / 2;
  98. }
  99. return getBytes(data).length;
  100. }
  101. /**
  102. * Returns a [[DataHexString]] by slicing %%data%% from the %%start%%
  103. * offset to the %%end%% offset.
  104. *
  105. * By default %%start%% is 0 and %%end%% is the length of %%data%%.
  106. */
  107. export function dataSlice(data, start, end) {
  108. const bytes = getBytes(data);
  109. if (end != null && end > bytes.length) {
  110. assert(false, "cannot slice beyond data bounds", "BUFFER_OVERRUN", {
  111. buffer: bytes, length: bytes.length, offset: end
  112. });
  113. }
  114. return hexlify(bytes.slice((start == null) ? 0 : start, (end == null) ? bytes.length : end));
  115. }
  116. /**
  117. * Return the [[DataHexString]] result by stripping all **leading**
  118. ** zero bytes from %%data%%.
  119. */
  120. export function stripZerosLeft(data) {
  121. let bytes = hexlify(data).substring(2);
  122. while (bytes.startsWith("00")) {
  123. bytes = bytes.substring(2);
  124. }
  125. return "0x" + bytes;
  126. }
  127. function zeroPad(data, length, left) {
  128. const bytes = getBytes(data);
  129. assert(length >= bytes.length, "padding exceeds data length", "BUFFER_OVERRUN", {
  130. buffer: new Uint8Array(bytes),
  131. length: length,
  132. offset: length + 1
  133. });
  134. const result = new Uint8Array(length);
  135. result.fill(0);
  136. if (left) {
  137. result.set(bytes, length - bytes.length);
  138. }
  139. else {
  140. result.set(bytes, 0);
  141. }
  142. return hexlify(result);
  143. }
  144. /**
  145. * Return the [[DataHexString]] of %%data%% padded on the **left**
  146. * to %%length%% bytes.
  147. *
  148. * If %%data%% already exceeds %%length%%, a [[BufferOverrunError]] is
  149. * thrown.
  150. *
  151. * This pads data the same as **values** are in Solidity
  152. * (e.g. ``uint128``).
  153. */
  154. export function zeroPadValue(data, length) {
  155. return zeroPad(data, length, true);
  156. }
  157. /**
  158. * Return the [[DataHexString]] of %%data%% padded on the **right**
  159. * to %%length%% bytes.
  160. *
  161. * If %%data%% already exceeds %%length%%, a [[BufferOverrunError]] is
  162. * thrown.
  163. *
  164. * This pads data the same as **bytes** are in Solidity
  165. * (e.g. ``bytes16``).
  166. */
  167. export function zeroPadBytes(data, length) {
  168. return zeroPad(data, length, false);
  169. }
  170. //# sourceMappingURL=data.js.map