sequence.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { toUint8Array, toUint8ArrayCopy } from "./buffer-source.js";
  2. function clampIndex(value, fallback, length) {
  3. const normalized = Number.isFinite(value) ? Math.trunc(value) : fallback;
  4. if (normalized <= 0) {
  5. return 0;
  6. }
  7. if (normalized >= length) {
  8. return length;
  9. }
  10. return normalized;
  11. }
  12. function normalizeForwardRange(length, options) {
  13. const start = clampIndex(options?.start, 0, length);
  14. const end = clampIndex(options?.end, length, length);
  15. return end >= start ? [start, end] : [start, start];
  16. }
  17. function normalizeReverseRange(length, options) {
  18. const start = clampIndex(options?.start, length, length);
  19. const end = clampIndex(options?.end, 0, length);
  20. return start >= end ? [end, start] : [start, start];
  21. }
  22. function normalizeSliceIndex(value, fallback, length) {
  23. const normalized = Number.isFinite(value) ? Math.trunc(value) : fallback;
  24. if (normalized < 0) {
  25. return Math.max(length + normalized, 0);
  26. }
  27. if (normalized > length) {
  28. return length;
  29. }
  30. return normalized;
  31. }
  32. function encodeAscii(text) {
  33. const bytes = new Uint8Array(text.length);
  34. for (let i = 0; i < text.length; i++) {
  35. bytes[i] = text.charCodeAt(i) & 0xff;
  36. }
  37. return bytes;
  38. }
  39. function encodeUtf8(text) {
  40. return new TextEncoder().encode(text);
  41. }
  42. function toPatternBytes(pattern, options) {
  43. if (typeof pattern === "string") {
  44. return options?.encoding === "utf8" ? encodeUtf8(pattern) : encodeAscii(pattern);
  45. }
  46. return toUint8Array(pattern);
  47. }
  48. function bytesEqualAt(data, pattern, offset) {
  49. for (let index = 0; index < pattern.byteLength; index++) {
  50. if (data[offset + index] !== pattern[index]) {
  51. return false;
  52. }
  53. }
  54. return true;
  55. }
  56. export function indexOf(data, pattern, options) {
  57. const bytes = toUint8Array(data);
  58. const needle = toPatternBytes(pattern, options);
  59. const [start, end] = normalizeForwardRange(bytes.byteLength, options);
  60. if (needle.byteLength === 0) {
  61. return start;
  62. }
  63. const lastOffset = end - needle.byteLength;
  64. if (lastOffset < start) {
  65. return -1;
  66. }
  67. for (let offset = start; offset <= lastOffset; offset++) {
  68. if (bytesEqualAt(bytes, needle, offset)) {
  69. return offset;
  70. }
  71. }
  72. return -1;
  73. }
  74. export function lastIndexOf(data, pattern, options) {
  75. const bytes = toUint8Array(data);
  76. const needle = toPatternBytes(pattern, options);
  77. const [end, start] = normalizeReverseRange(bytes.byteLength, options);
  78. if (needle.byteLength === 0) {
  79. return start;
  80. }
  81. const firstOffset = start - needle.byteLength;
  82. if (firstOffset < end) {
  83. return -1;
  84. }
  85. for (let offset = firstOffset; offset >= end; offset--) {
  86. if (bytesEqualAt(bytes, needle, offset)) {
  87. return offset;
  88. }
  89. }
  90. return -1;
  91. }
  92. export function includes(data, pattern, options) {
  93. return indexOf(data, pattern, options) !== -1;
  94. }
  95. export function startsWith(data, pattern, options) {
  96. const bytes = toUint8Array(data);
  97. const needle = toPatternBytes(pattern, options);
  98. if (needle.byteLength > bytes.byteLength) {
  99. return false;
  100. }
  101. return bytesEqualAt(bytes, needle, 0);
  102. }
  103. export function endsWith(data, pattern, options) {
  104. const bytes = toUint8Array(data);
  105. const needle = toPatternBytes(pattern, options);
  106. if (needle.byteLength > bytes.byteLength) {
  107. return false;
  108. }
  109. return bytesEqualAt(bytes, needle, bytes.byteLength - needle.byteLength);
  110. }
  111. export function slice(data, start, end) {
  112. const bytes = toUint8Array(data);
  113. const normalizedStart = normalizeSliceIndex(start, 0, bytes.byteLength);
  114. const normalizedEnd = normalizeSliceIndex(end, bytes.byteLength, bytes.byteLength);
  115. if (normalizedEnd <= normalizedStart) {
  116. return bytes.subarray(normalizedStart, normalizedStart);
  117. }
  118. return bytes.subarray(normalizedStart, normalizedEnd);
  119. }
  120. export function tail(data, length) {
  121. const bytes = toUint8Array(data);
  122. const normalizedLength = Number.isFinite(length) ? Math.max(0, Math.trunc(length)) : 0;
  123. if (normalizedLength >= bytes.byteLength) {
  124. return bytes;
  125. }
  126. return bytes.subarray(bytes.byteLength - normalizedLength);
  127. }
  128. export function copy(data) {
  129. return toUint8ArrayCopy(data);
  130. }
  131. export function compare(a, b) {
  132. const left = toUint8Array(a);
  133. const right = toUint8Array(b);
  134. const limit = Math.min(left.byteLength, right.byteLength);
  135. for (let index = 0; index < limit; index++) {
  136. if (left[index] < right[index]) {
  137. return -1;
  138. }
  139. if (left[index] > right[index]) {
  140. return 1;
  141. }
  142. }
  143. if (left.byteLength < right.byteLength) {
  144. return -1;
  145. }
  146. if (left.byteLength > right.byteLength) {
  147. return 1;
  148. }
  149. return 0;
  150. }