@amritk/nish-x86_64-linux 0.27.2 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -160,85 +160,98 @@ export class Crc32 {
160
160
  if (toI32(t.length) !== CRC32_TABLE_SIZE || toI32(bt.length) !== CRC32_TABLE_SIZE) {
161
161
  panic("Crc32.update: the table is not its size")
162
162
  }
163
- let c: u32 = ~crc
164
- let p: i32 = off
165
- const end: i32 = off + len
166
- if (len >= CRC32_BRAID_MIN) {
167
- // Braided (Kadatch and Jenkins, "Everything we know about CRC but
168
- // afraid to forget", 2010): five chains, each over every fifth 8-byte
169
- // word, so a step's table reads overlap the other four's instead of
170
- // waiting on its own last. A braid's table also steps it over the
171
- // other braids' 32 bytes, so before each block every register sits at
172
- // its own word's place. The last block is taken one word at a time
173
- // with the plain table, each braid's register joining as its word
174
- // comes, which adds them into one.
175
- const blocks: i32 = len / CRC32_BLOCK
176
- const lastBlock: i32 = off + (blocks - 1) * CRC32_BLOCK
177
- let c1: u32 = 0
178
- let c2: u32 = 0
179
- let c3: u32 = 0
180
- let c4: u32 = 0
181
- // Each word's position is a local the loop condition bounds, which
182
- // is what lets it enter the loader's range without a check.
183
- let p1: i32 = p + 8
184
- let p2: i32 = p + 16
185
- let p3: i32 = p + 24
186
- let p4: i32 = p + 32
187
- while (
188
- p < lastBlock &&
189
- p >= 0 &&
190
- p1 >= 0 &&
191
- p2 >= 0 &&
192
- p3 >= 0 &&
193
- p4 >= 0 &&
194
- p4 <= 2147483639 &&
195
- p3 <= 2147483639 &&
196
- p2 <= 2147483639 &&
197
- p1 <= 2147483639 &&
198
- p <= 2147483639
199
- ) {
200
- c = crc32Step(bt, c, checksumLoad64(buf, p))
201
- c1 = crc32Step(bt, c1, checksumLoad64(buf, p1))
202
- c2 = crc32Step(bt, c2, checksumLoad64(buf, p2))
203
- c3 = crc32Step(bt, c3, checksumLoad64(buf, p3))
204
- c4 = crc32Step(bt, c4, checksumLoad64(buf, p4))
205
- p += CRC32_BLOCK
206
- p1 += CRC32_BLOCK
207
- p2 += CRC32_BLOCK
208
- p3 += CRC32_BLOCK
209
- p4 += CRC32_BLOCK
210
- }
211
- if (
212
- p >= 0 &&
213
- p1 >= 0 &&
214
- p2 >= 0 &&
215
- p3 >= 0 &&
216
- p4 >= 0 &&
217
- p4 <= 2147483639 &&
218
- p3 <= 2147483639 &&
219
- p2 <= 2147483639 &&
220
- p1 <= 2147483639 &&
221
- p <= 2147483639
222
- ) {
223
- c = crc32Step(t, c, checksumLoad64(buf, p))
224
- c = crc32Step(t, c ^ c1, checksumLoad64(buf, p1))
225
- c = crc32Step(t, c ^ c2, checksumLoad64(buf, p2))
226
- c = crc32Step(t, c ^ c3, checksumLoad64(buf, p3))
227
- c = crc32Step(t, c ^ c4, checksumLoad64(buf, p4))
228
- p += CRC32_BLOCK
229
- }
163
+ return crc32Walk(t, bt, crc, buf, off, len)
164
+ }
165
+ }
166
+
167
+ /**
168
+ * `Crc32.update`'s loop: the CRC-32 of the bytes `crc` was the CRC of,
169
+ * followed by `buf[off .. off + len)`, a window its one caller has checked,
170
+ * as it has checked both tables' length. Natively the compiler puts the call
171
+ * behind a branch on `nish_crc32_hw` and hands the same window to the
172
+ * runtime's `nish_crc32_update`, which runs it on PCLMULQDQ or the ARMv8
173
+ * CRC32 instructions (`src/kernels.ts`); a machine without them, a process
174
+ * run with `NISH_CRC32=soft`, a wasm32 build and Node run this.
175
+ */
176
+ const crc32Walk = (t: u32[], bt: u32[], crc: u32, buf: u8[], off: i32, len: i32): u32 => {
177
+ let c: u32 = ~crc
178
+ let p: i32 = off
179
+ const end: i32 = off + len
180
+ if (len >= CRC32_BRAID_MIN) {
181
+ // Braided (Kadatch and Jenkins, "Everything we know about CRC but
182
+ // afraid to forget", 2010): five chains, each over every fifth 8-byte
183
+ // word, so a step's table reads overlap the other four's instead of
184
+ // waiting on its own last. A braid's table also steps it over the
185
+ // other braids' 32 bytes, so before each block every register sits at
186
+ // its own word's place. The last block is taken one word at a time
187
+ // with the plain table, each braid's register joining as its word
188
+ // comes, which adds them into one.
189
+ const blocks: i32 = len / CRC32_BLOCK
190
+ const lastBlock: i32 = off + (blocks - 1) * CRC32_BLOCK
191
+ let c1: u32 = 0
192
+ let c2: u32 = 0
193
+ let c3: u32 = 0
194
+ let c4: u32 = 0
195
+ // Each word's position is a local the loop condition bounds, which
196
+ // is what lets it enter the loader's range without a check.
197
+ let p1: i32 = p + 8
198
+ let p2: i32 = p + 16
199
+ let p3: i32 = p + 24
200
+ let p4: i32 = p + 32
201
+ while (
202
+ p < lastBlock &&
203
+ p >= 0 &&
204
+ p1 >= 0 &&
205
+ p2 >= 0 &&
206
+ p3 >= 0 &&
207
+ p4 >= 0 &&
208
+ p4 <= 2147483639 &&
209
+ p3 <= 2147483639 &&
210
+ p2 <= 2147483639 &&
211
+ p1 <= 2147483639 &&
212
+ p <= 2147483639
213
+ ) {
214
+ c = crc32Step(bt, c, checksumLoad64(buf, p))
215
+ c1 = crc32Step(bt, c1, checksumLoad64(buf, p1))
216
+ c2 = crc32Step(bt, c2, checksumLoad64(buf, p2))
217
+ c3 = crc32Step(bt, c3, checksumLoad64(buf, p3))
218
+ c4 = crc32Step(bt, c4, checksumLoad64(buf, p4))
219
+ p += CRC32_BLOCK
220
+ p1 += CRC32_BLOCK
221
+ p2 += CRC32_BLOCK
222
+ p3 += CRC32_BLOCK
223
+ p4 += CRC32_BLOCK
230
224
  }
231
- const whole: i32 = end - (len & 7)
232
- while (p < whole && p >= 0 && p <= 2147483639) {
225
+ if (
226
+ p >= 0 &&
227
+ p1 >= 0 &&
228
+ p2 >= 0 &&
229
+ p3 >= 0 &&
230
+ p4 >= 0 &&
231
+ p4 <= 2147483639 &&
232
+ p3 <= 2147483639 &&
233
+ p2 <= 2147483639 &&
234
+ p1 <= 2147483639 &&
235
+ p <= 2147483639
236
+ ) {
233
237
  c = crc32Step(t, c, checksumLoad64(buf, p))
234
- p += 8
235
- }
236
- while (p < end) {
237
- c = uncheckedGet(t, toI32((c ^ toU32(uncheckedGet(buf, p))) & 255)) ^ (c >>> 8)
238
- p += 1
238
+ c = crc32Step(t, c ^ c1, checksumLoad64(buf, p1))
239
+ c = crc32Step(t, c ^ c2, checksumLoad64(buf, p2))
240
+ c = crc32Step(t, c ^ c3, checksumLoad64(buf, p3))
241
+ c = crc32Step(t, c ^ c4, checksumLoad64(buf, p4))
242
+ p += CRC32_BLOCK
239
243
  }
240
- return ~c
241
244
  }
245
+ const whole: i32 = end - (len & 7)
246
+ while (p < whole && p >= 0 && p <= 2147483639) {
247
+ c = crc32Step(t, c, checksumLoad64(buf, p))
248
+ p += 8
249
+ }
250
+ while (p < end) {
251
+ c = uncheckedGet(t, toI32((c ^ toU32(uncheckedGet(buf, p))) & 255)) ^ (c >>> 8)
252
+ p += 1
253
+ }
254
+ return ~c
242
255
  }
243
256
 
244
257
  /**
@@ -128,6 +128,12 @@ const DEFLATE_STORED_MAX: i32 = 65535
128
128
  /** Huffman's multiplier for the hash (Knuth's 2^32 / phi). */
129
129
  const DEFLATE_HASH_MUL: i64 = 2654435761
130
130
 
131
+ /** `FixedDeflater`'s farthest match back: its inputs are short. */
132
+ const FIXED_WINDOW: i32 = 16384
133
+
134
+ /** `FixedDeflater`'s largest hash table, 2^14 entries; a call clears only as much as its input needs. */
135
+ const FIXED_HASH_BITS: i32 = 14
136
+
131
137
  /** Decoder table entry kinds, in bits 8 to 10 of an entry. */
132
138
  const INF_LITERAL: i32 = 0
133
139
  const INF_LENGTH: i32 = 1
@@ -1653,6 +1659,224 @@ export class Deflater {
1653
1659
  }
1654
1660
  }
1655
1661
 
1662
+ /**
1663
+ * The most bytes `FixedDeflater.compress` writes for `n` bytes of input: nine
1664
+ * bits a byte, the block's header and end code, and the eight bytes of slack
1665
+ * its word-wide bit writer stores past the last whole byte. A negative `n`
1666
+ * answers the bound of 0.
1667
+ */
1668
+ export const fixedDeflateBound = (n: i32): i32 => {
1669
+ if (n < 0) {
1670
+ return 16
1671
+ }
1672
+ return n + (n >> 3) + 16
1673
+ }
1674
+
1675
+ /**
1676
+ * A one-block DEFLATE compressor in the fixed Huffman code (BTYPE=01, §3.2.6)
1677
+ * for short inputs such as an HTTP response body: one hash probe per position,
1678
+ * matches at most `FIXED_WINDOW` back, no code to build or send. It carries
1679
+ * nothing from one call to the next: the hash table, sized to the input, is
1680
+ * cleared at the start of every call, so the output is a function of the
1681
+ * input alone. About 70 KB of tables; reuse one per thread.
1682
+ */
1683
+ export class FixedDeflater {
1684
+ /** `head[h]`: one past the last position, from the input's start, whose four bytes hash to `h`; 0 for none. */
1685
+ readonly head: i32[]
1686
+ /** Per byte: its fixed code, bit-reversed, below bit 16, and the code's length above. */
1687
+ readonly literal: i32[]
1688
+ /** Per length - 3: the length symbol's code and then its extra bits, below bit 16, and how many bits above. */
1689
+ readonly length: i32[]
1690
+ /** Distance to distance symbol, as `Deflater.distSymbol` maps it. */
1691
+ readonly distSymbol: i32[]
1692
+ /** Per distance symbol: its five-bit code, bit-reversed; its base distance; its extra bits. */
1693
+ readonly distCode: i32[]
1694
+ readonly distBase: i32[]
1695
+ readonly distExtra: i32[]
1696
+
1697
+ /** The tables, once. */
1698
+ constructor() {
1699
+ this.head = new Array<i32>(1 << FIXED_HASH_BITS)
1700
+ this.literal = new Array<i32>(256)
1701
+ this.length = new Array<i32>(256)
1702
+ this.distSymbol = new Array<i32>(512)
1703
+ this.distCode = new Array<i32>(30)
1704
+ this.distBase = new Array<i32>(30)
1705
+ this.distExtra = new Array<i32>(30)
1706
+ const lens: i32[] = new Array<i32>(288)
1707
+ const codes: i32[] = new Array<i32>(288)
1708
+ const count: i32[] = new Array<i32>(16)
1709
+ const next: i32[] = new Array<i32>(16)
1710
+ for (let s: i32 = 0; s < 288; s += 1) {
1711
+ lens[s] = deflateFixedLitLength(s)
1712
+ }
1713
+ deflateCanonical(lens, 288, codes, count, next)
1714
+ for (let b: i32 = 0; b < 256; b += 1) {
1715
+ this.literal[b] = codes[b] | (lens[b] << 16)
1716
+ }
1717
+ for (let s: i32 = 0; s < 29; s += 1) {
1718
+ const base: i32 = deflateLengthBase(s)
1719
+ const extra: i32 = deflateLengthExtra(s)
1720
+ // Symbol 27 stops short of 258, which has symbol 28 to itself.
1721
+ const top: i32 = s === 28 ? 259 : deflateLengthBase(s + 1)
1722
+ for (let l: i32 = base; l < top && l >= 3 && l <= 258; l += 1) {
1723
+ const n: i32 = lens[257 + s]
1724
+ this.length[l - 3] = codes[257 + s] | ((l - base) << n) | ((n + extra) << 16)
1725
+ }
1726
+ }
1727
+ for (let d: i32 = 0; d < 30; d += 1) {
1728
+ this.distCode[d] = deflateReverse(d, 5)
1729
+ this.distBase[d] = deflateDistBase(d)
1730
+ this.distExtra[d] = deflateDistExtra(d)
1731
+ }
1732
+ // Distances 1 to 256 directly; above, by (distance - 1) >> 7 from 256 on.
1733
+ for (let d: i32 = 0; d < 30; d += 1) {
1734
+ const lo: i32 = this.distBase[d]
1735
+ const hi: i32 = d === 29 ? 32769 : this.distBase[d + 1]
1736
+ for (let dist: i32 = lo; dist < hi; dist += 1) {
1737
+ if (dist <= 256) {
1738
+ this.distSymbol[dist - 1] = d
1739
+ } else {
1740
+ const at: i32 = ((dist - 1) >> 7) + 256
1741
+ if (at < 512) {
1742
+ this.distSymbol[at] = d
1743
+ }
1744
+ }
1745
+ }
1746
+ }
1747
+ }
1748
+
1749
+ /**
1750
+ * Compresses `src[off .. off + len)` as one final fixed-Huffman block into
1751
+ * `out[at .. at + room)`, and answers where it ends, or `DEFLATE_NO_ROOM` as
1752
+ * soon as it would need more than `room` bytes: a caller that only wants
1753
+ * the output when it is smaller passes the most it will take, and an input
1754
+ * that does not compress is abandoned part way. `fixedDeflateBound(len)`
1755
+ * always fits. A window outside its array panics. Allocates nothing.
1756
+ */
1757
+ compress(src: u8[], off: i32, len: i32, out: u8[], at: i32, room: i32): i32 {
1758
+ deflateWindow("FixedDeflater.compress", src, off, len)
1759
+ deflateWindow("FixedDeflater.compress", out, at, room)
1760
+ const head: i32[] = this.head
1761
+ const literal: i32[] = this.literal
1762
+ const length: i32[] = this.length
1763
+ const dsym: i32[] = this.distSymbol
1764
+ const dcode: i32[] = this.distCode
1765
+ const dbase: i32[] = this.distBase
1766
+ const dextra: i32[] = this.distExtra
1767
+ if (
1768
+ toI32(head.length) !== 1 << FIXED_HASH_BITS ||
1769
+ toI32(literal.length) !== 256 ||
1770
+ toI32(length.length) !== 256 ||
1771
+ toI32(dsym.length) !== 512 ||
1772
+ toI32(dcode.length) !== 30 ||
1773
+ toI32(dbase.length) !== 30 ||
1774
+ toI32(dextra.length) !== 30
1775
+ ) {
1776
+ panic("FixedDeflater.compress: a table is not its size")
1777
+ }
1778
+ // A table about the input's size: clearing it is most of a short call's
1779
+ // fixed cost, and a larger one finds no more matches.
1780
+ let bits: i32 = 8
1781
+ while (bits < FIXED_HASH_BITS && 1 << bits < len) {
1782
+ bits += 1
1783
+ }
1784
+ head.fill(0, 0, 1 << bits)
1785
+ const shift: u32 = toU32(32 - bits)
1786
+ const end: i32 = off + len
1787
+ // Matches start where eight bytes can be read; the last few are literals.
1788
+ const last: i32 = end - 8
1789
+ // A flush writes four bytes of the final output, so one that would pass
1790
+ // the room's end means the whole cannot fit.
1791
+ const oe: i32 = at + room - 4
1792
+ // BFINAL = 1, BTYPE = 01.
1793
+ let bb: u64 = toU64(3)
1794
+ let bc: i32 = 3
1795
+ let o: i32 = at
1796
+ let p: i32 = off
1797
+ while (p < end) {
1798
+ let matched: i32 = 0
1799
+ let dist: i32 = 0
1800
+ if (p >= 0 && p <= last && p <= 2147483639) {
1801
+ const pi: integer<0, 2147483639> = p
1802
+ const w: u64 = deflateLoad64(src, pi)
1803
+ const h: i32 = toI32((toU32(w) * toU32(DEFLATE_HASH_MUL)) >>> shift)
1804
+ const c: i32 = uncheckedGet(head, h) - 1 + off
1805
+ uncheckedSet(head, h, p - off + 1)
1806
+ dist = p - c
1807
+ if (c >= off && c >= 0 && c <= 2147483639 && dist <= FIXED_WINDOW) {
1808
+ const ci: integer<0, 2147483639> = c
1809
+ const x: u64 = deflateLoad64(src, ci) ^ w
1810
+ if (toU32(x) === toU32(0)) {
1811
+ const max: i32 = end - p < DEFLATE_MAX_MATCH ? end - p : DEFLATE_MAX_MATCH
1812
+ matched =
1813
+ x !== toU64(0)
1814
+ ? deflateCtz64(x) >> 3
1815
+ : toI32(8) + deflateMatchLength(src, c + 8, p + 8, max - 8)
1816
+ }
1817
+ }
1818
+ }
1819
+ if (matched === 0) {
1820
+ const lc: i32 = uncheckedGet(literal, toI32(uncheckedGet(src, p)))
1821
+ bb = bb | (toU64(toU32(lc & 0xffff)) << toU64(bc))
1822
+ bc += lc >> 16
1823
+ p += 1
1824
+ } else {
1825
+ const lc: i32 = uncheckedGet(length, matched - 3)
1826
+ bb = bb | (toU64(toU32(lc & 0xffff)) << toU64(bc))
1827
+ bc += lc >> 16
1828
+ if (bc >= 32) {
1829
+ if (o > oe) {
1830
+ return DEFLATE_NO_ROOM
1831
+ }
1832
+ uncheckedSet(out, o, toU8(bb))
1833
+ uncheckedSet(out, o + 1, toU8(bb >> toU64(8)))
1834
+ uncheckedSet(out, o + 2, toU8(bb >> toU64(16)))
1835
+ uncheckedSet(out, o + 3, toU8(bb >> toU64(24)))
1836
+ bb = bb >> toU64(32)
1837
+ bc -= 32
1838
+ o += 4
1839
+ }
1840
+ const d: i32 =
1841
+ dist <= 256 ? uncheckedGet(dsym, dist - 1) : uncheckedGet(dsym, ((dist - 1) >> 7) + 256)
1842
+ bb = bb | (toU64(toU32(uncheckedGet(dcode, d) | ((dist - uncheckedGet(dbase, d)) << 5))) << toU64(bc))
1843
+ bc += 5 + uncheckedGet(dextra, d)
1844
+ // One position near the match's end goes into the table, for the
1845
+ // next repeat of what follows it; the rest are skipped.
1846
+ const q: i32 = p + matched - 2
1847
+ if (q >= 0 && q <= last && q <= 2147483639) {
1848
+ const qi: integer<0, 2147483639> = q
1849
+ const hq: i32 = toI32((deflateLoad32(src, qi) * toU32(DEFLATE_HASH_MUL)) >>> shift)
1850
+ uncheckedSet(head, hq, q - off + 1)
1851
+ }
1852
+ p += matched
1853
+ }
1854
+ if (bc >= 32) {
1855
+ if (o > oe) {
1856
+ return DEFLATE_NO_ROOM
1857
+ }
1858
+ uncheckedSet(out, o, toU8(bb))
1859
+ uncheckedSet(out, o + 1, toU8(bb >> toU64(8)))
1860
+ uncheckedSet(out, o + 2, toU8(bb >> toU64(16)))
1861
+ uncheckedSet(out, o + 3, toU8(bb >> toU64(24)))
1862
+ bb = bb >> toU64(32)
1863
+ bc -= 32
1864
+ o += 4
1865
+ }
1866
+ }
1867
+ // The end-of-block code, 256: seven zero bits.
1868
+ bc += 7
1869
+ const bytes: i32 = (bc + 7) >> 3
1870
+ if (o > at + room - bytes) {
1871
+ return DEFLATE_NO_ROOM
1872
+ }
1873
+ for (let i: i32 = 0; i < bytes; i += 1) {
1874
+ uncheckedSet(out, o + i, toU8(bb >> toU64(8 * i)))
1875
+ }
1876
+ return o + bytes
1877
+ }
1878
+ }
1879
+
1656
1880
  /**
1657
1881
  * One Huffman decoding table: the entries for every `bits`-bit prefix, and
1658
1882
  * for the codes longer than that, the count per length and the symbols in
@@ -40,11 +40,13 @@ import {
40
40
  DEFLATE_ERR_TRUNCATED,
41
41
  DEFLATE_NO_ROOM,
42
42
  Deflater,
43
+ FixedDeflater,
43
44
  INFLATE_DONE,
44
45
  INFLATE_NEED_INPUT,
45
46
  INFLATE_NEED_OUTPUT,
46
47
  Inflater,
47
48
  deflateBound,
49
+ fixedDeflateBound,
48
50
  } from "nish/compress/deflate"
49
51
  import { copyBytes } from "nish/bytes"
50
52
 
@@ -203,6 +205,56 @@ export class GzipCompressor {
203
205
  }
204
206
  }
205
207
 
208
+ /** The most bytes `GzipFixedCompressor.compress` writes for `n` bytes of input. */
209
+ export const gzipFixedBound = (n: i32): i32 => fixedDeflateBound(n) + GZIP_HEADER_SIZE + GZIP_TRAILER_SIZE
210
+
211
+ /**
212
+ * A gzip compressor for short inputs: a `FixedDeflater` (one fixed-Huffman
213
+ * block, 16 KB window) and the CRC-32 tables, allocated once. Each call
214
+ * depends on its input alone. Not shared between threads.
215
+ */
216
+ export class GzipFixedCompressor {
217
+ readonly deflater: FixedDeflater
218
+ readonly crc: Crc32
219
+
220
+ /** Builds the tables. */
221
+ constructor() {
222
+ this.deflater = new FixedDeflater()
223
+ this.crc = new Crc32()
224
+ }
225
+
226
+ /**
227
+ * Compresses `src[off .. off + len)` into `out[at .. at + room)` as one gzip
228
+ * member, and answers where it ends, or `DEFLATE_NO_ROOM` (-1) as soon as
229
+ * it would need more than `room` bytes, so a caller passing the most it
230
+ * will take stops early on an input that does not shrink.
231
+ * `gzipFixedBound(len)` always fits. A window outside its array panics.
232
+ * Allocates nothing.
233
+ */
234
+ compress(src: u8[], off: i32, len: i32, out: u8[], at: i32, room: i32): i32 {
235
+ gzipWindow("GzipFixedCompressor.compress", src, off, len)
236
+ gzipWindow("GzipFixedCompressor.compress", out, at, room)
237
+ if (room < GZIP_HEADER_SIZE + GZIP_TRAILER_SIZE) {
238
+ return DEFLATE_NO_ROOM
239
+ }
240
+ gzipWriteHeader(out, at, 1)
241
+ const end: i32 = this.deflater.compress(
242
+ src,
243
+ off,
244
+ len,
245
+ out,
246
+ at + GZIP_HEADER_SIZE,
247
+ room - GZIP_HEADER_SIZE - GZIP_TRAILER_SIZE
248
+ )
249
+ if (end < 0) {
250
+ return DEFLATE_NO_ROOM
251
+ }
252
+ gzipPut32(out, end, this.crc.update(toU32(0), src, off, len))
253
+ gzipPut32(out, end + 4, toU32(len))
254
+ return end + GZIP_TRAILER_SIZE
255
+ }
256
+ }
257
+
206
258
  /**
207
259
  * A gzip decompressor: an `Inflater` and the CRC-32 tables, allocated once.
208
260
  * Not shared between threads.