initial commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
--- a/crypto/mem.cc
|
||||
+++ b/crypto/mem.cc
|
||||
@@ -100,9 +100,20 @@
|
||||
// primitives used must tolerate every other synchronization primitive linked
|
||||
// into the process, including pthreads locks. Failing to meet these constraints
|
||||
// may result in deadlocks, crashes, or memory corruption.
|
||||
+#if defined(BORINGSSL_REQUIRE_MEMORY_HOOKS)
|
||||
+// Bun: the embedder guarantees these three are always defined. WEAK_SYMBOL_FUNC
|
||||
+// is gated on __ELF__, so on Mach-O and COFF it would expand to a static null
|
||||
+// pointer and the hook path would be folded away at compile time.
|
||||
+extern "C" {
|
||||
+void *OPENSSL_memory_alloc(size_t size);
|
||||
+void OPENSSL_memory_free(void *ptr);
|
||||
+size_t OPENSSL_memory_get_size(void *ptr);
|
||||
+}
|
||||
+#else
|
||||
WEAK_SYMBOL_FUNC(void *, OPENSSL_memory_alloc, (size_t size))
|
||||
WEAK_SYMBOL_FUNC(void, OPENSSL_memory_free, (void *ptr))
|
||||
WEAK_SYMBOL_FUNC(size_t, OPENSSL_memory_get_size, (void *ptr))
|
||||
+#endif
|
||||
|
||||
#if defined(BORINGSSL_MALLOC_FAILURE_TESTING)
|
||||
static StaticMutex malloc_failure_lock;
|
||||
@@ -186,6 +197,15 @@
|
||||
goto err;
|
||||
}
|
||||
|
||||
+#if defined(BORINGSSL_REQUIRE_MEMORY_HOOKS)
|
||||
+ {
|
||||
+ void *ptr2 = OPENSSL_memory_alloc(size);
|
||||
+ if (ptr2 == nullptr && size != 0) {
|
||||
+ goto err;
|
||||
+ }
|
||||
+ return ptr2;
|
||||
+ }
|
||||
+#else
|
||||
if (OPENSSL_memory_alloc != nullptr) {
|
||||
assert(OPENSSL_memory_free != nullptr);
|
||||
assert(OPENSSL_memory_get_size != nullptr);
|
||||
@@ -195,6 +215,7 @@
|
||||
}
|
||||
return ptr2;
|
||||
}
|
||||
+#endif
|
||||
|
||||
if (size + OPENSSL_MALLOC_PREFIX < size) {
|
||||
goto err;
|
||||
@@ -238,10 +259,15 @@
|
||||
return;
|
||||
}
|
||||
|
||||
+#if defined(BORINGSSL_REQUIRE_MEMORY_HOOKS)
|
||||
+ OPENSSL_memory_free(orig_ptr);
|
||||
+ return;
|
||||
+#else
|
||||
if (OPENSSL_memory_free != nullptr) {
|
||||
OPENSSL_memory_free(orig_ptr);
|
||||
return;
|
||||
}
|
||||
+#endif
|
||||
|
||||
void *ptr = ((uint8_t *)orig_ptr) - OPENSSL_MALLOC_PREFIX;
|
||||
__asan_unpoison_memory_region(ptr, OPENSSL_MALLOC_PREFIX);
|
||||
@@ -268,6 +294,9 @@
|
||||
}
|
||||
|
||||
size_t old_size;
|
||||
+#if defined(BORINGSSL_REQUIRE_MEMORY_HOOKS)
|
||||
+ old_size = OPENSSL_memory_get_size(orig_ptr);
|
||||
+#else
|
||||
if (OPENSSL_memory_get_size != nullptr) {
|
||||
old_size = OPENSSL_memory_get_size(orig_ptr);
|
||||
} else {
|
||||
@@ -276,6 +305,7 @@
|
||||
old_size = *(size_t *)ptr;
|
||||
__asan_poison_memory_region(ptr, OPENSSL_MALLOC_PREFIX);
|
||||
}
|
||||
+#endif
|
||||
|
||||
void *ret = OPENSSL_malloc(new_size);
|
||||
if (ret == nullptr) {
|
||||
@@ -0,0 +1,36 @@
|
||||
Accept DNS name compression in RDATA on parse.
|
||||
|
||||
c-ares 1.34.8 started rejecting compression pointers inside RDATA for RR
|
||||
types that RFC 3597 says must not use them (SRV, NAPTR, etc.). That is
|
||||
correct for writers, but a large share of deployed resolvers and caches
|
||||
(older BIND, dnsmasq, mDNSResponder on macOS, assorted corporate
|
||||
forwarders) still compress SRV targets on the wire, so dns.resolveSrv()
|
||||
fails with EBADRESP against those servers.
|
||||
|
||||
c-ares already refuses to emit compression for these types
|
||||
(ares_dns_rec_allow_name_comp gates the writer). This patch keeps the
|
||||
parser lenient the way it was before 1.34.8 so existing servers keep
|
||||
working, while the writer remains strict.
|
||||
|
||||
--- a/src/lib/record/ares_dns_parse.c
|
||||
+++ b/src/lib/record/ares_dns_parse.c
|
||||
@@ -46,14 +46,12 @@
|
||||
{
|
||||
ares_status_t status;
|
||||
char *name = NULL;
|
||||
- /* Only RR types defined in RFC1035 may use name compression within their
|
||||
- * RDATA (RFC3597). Reject compression pointers for any other type (e.g.
|
||||
- * SRV per RFC2782) to match the write-side policy and avoid following
|
||||
- * pointers that a non-understanding nameserver could not have rewritten. */
|
||||
- ares_bool_t allow_compression =
|
||||
- ares_dns_rec_allow_name_comp(ares_dns_rr_get_type(rr));
|
||||
|
||||
- status = ares_dns_name_parse(buf, &name, is_hostname, allow_compression);
|
||||
+ /* Bun: accept compression in RDATA on parse regardless of RR type.
|
||||
+ * RFC 3597 says writers must not compress here and the c-ares writer
|
||||
+ * already enforces that, but plenty of real resolvers still emit
|
||||
+ * compressed SRV/NAPTR targets, so stay lenient when reading. */
|
||||
+ status = ares_dns_name_parse(buf, &name, is_hostname, ARES_TRUE);
|
||||
if (status != ARES_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/src/hdr_histogram.c
|
||||
+++ b/src/hdr_histogram.c
|
||||
@@ -144,7 +144,7 @@ static int64_t counts_get_normalised(const struct hdr_histogram* h, int32_t inde
|
||||
static int32_t count_leading_zeros_64(int64_t value)
|
||||
{
|
||||
#if defined(_MSC_VER) && !(defined(__clang__) && (defined(_M_ARM) || defined(_M_ARM64)))
|
||||
- uint32_t leading_zero = 0;
|
||||
+ unsigned long leading_zero = 0;
|
||||
#if defined(_WIN64)
|
||||
_BitScanReverse64(&leading_zero, value);
|
||||
#else
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/hwy/base.h
|
||||
+++ b/hwy/base.h
|
||||
@@ -351,8 +351,7 @@ HWY_DLLEXPORT HWY_NORETURN void HWY_FORMAT(3, 4)
|
||||
|
||||
#endif // HWY_HEADER_ONLY
|
||||
|
||||
-#define HWY_WARN(format, ...) \
|
||||
- ::hwy::Warn(__FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
+#define HWY_WARN(format, ...)
|
||||
|
||||
#define HWY_ABORT(format, ...) \
|
||||
::hwy::Abort(__FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
--- a/hwy/contrib/thread_pool/topology.cc
|
||||
+++ b/hwy/contrib/thread_pool/topology.cc
|
||||
@@ -165,7 +165,7 @@ void ForeachBit(size_t num_groups, const GROUP_AFFINITY* affinity,
|
||||
size_t lp = group * 64 + Num0BitsBelowLS1Bit_Nonzero64(bits);
|
||||
bits &= bits - 1; // clear LSB
|
||||
if (HWY_UNLIKELY(lp >= lps.size())) {
|
||||
- Warn(__FILE__, line, "Clamping lp %zu to lps.size() %zu, groups %zu\n",
|
||||
+ HWY_WARN("Clamping lp %zu to lps.size() %zu, groups %zu\n",
|
||||
lp, lps.size(), num_groups);
|
||||
lp = lps.size() - 1;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
--- a/libarchive/archive_string.c
|
||||
+++ b/libarchive/archive_string.c
|
||||
@@ -1551,10 +1551,10 @@ my_atoi(const char *p)
|
||||
}
|
||||
|
||||
/*
|
||||
- * Return ANSI Code Page of current locale set by setlocale().
|
||||
+ * Return ANSI Code Page of current locale set by setlocale() (uncached).
|
||||
*/
|
||||
static unsigned
|
||||
-get_current_codepage(void)
|
||||
+get_current_codepage_uncached(void)
|
||||
{
|
||||
char *locale, *p;
|
||||
unsigned cp;
|
||||
@@ -1574,6 +1574,28 @@ get_current_codepage(void)
|
||||
return (cp);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Bun: cache the codepage after the first call. The process never changes
|
||||
+ * its locale at runtime, but `bun install` creates a charset converter per
|
||||
+ * extracted archive on many worker threads at once, and hammering the UCRT's
|
||||
+ * setlocale(LC_CTYPE, NULL) query from all of them concurrently is both
|
||||
+ * wasted work and a needless trip through the CRT locale machinery (and its
|
||||
+ * heap) on every archive. Compute once; later callers read the cached value.
|
||||
+ */
|
||||
+static unsigned
|
||||
+get_current_codepage(void)
|
||||
+{
|
||||
+ static volatile LONG cached_cp = -1;
|
||||
+ LONG cp = cached_cp;
|
||||
+
|
||||
+ if (cp == -1) {
|
||||
+ cp = (LONG)get_current_codepage_uncached();
|
||||
+ InterlockedCompareExchange(&cached_cp, cp, -1);
|
||||
+ cp = cached_cp;
|
||||
+ }
|
||||
+ return ((unsigned)cp);
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Translation table between Locale Name and ACP/OEMCP.
|
||||
*/
|
||||
@@ -1626,10 +1648,10 @@ static struct {
|
||||
};
|
||||
|
||||
/*
|
||||
- * Return OEM Code Page of current locale set by setlocale().
|
||||
+ * Return OEM Code Page of current locale set by setlocale() (uncached).
|
||||
*/
|
||||
static unsigned
|
||||
-get_current_oemcp(void)
|
||||
+get_current_oemcp_uncached(void)
|
||||
{
|
||||
int i;
|
||||
char *locale, *p;
|
||||
@@ -1652,6 +1674,23 @@ get_current_oemcp(void)
|
||||
}
|
||||
return (GetOEMCP());
|
||||
}
|
||||
+
|
||||
+/*
|
||||
+ * Bun: cached for the same reason as get_current_codepage() above.
|
||||
+ */
|
||||
+static unsigned
|
||||
+get_current_oemcp(void)
|
||||
+{
|
||||
+ static volatile LONG cached_ocp = -1;
|
||||
+ LONG ocp = cached_ocp;
|
||||
+
|
||||
+ if (ocp == -1) {
|
||||
+ ocp = (LONG)get_current_oemcp_uncached();
|
||||
+ InterlockedCompareExchange(&cached_ocp, ocp, -1);
|
||||
+ ocp = cached_ocp;
|
||||
+ }
|
||||
+ return ((unsigned)ocp);
|
||||
+}
|
||||
#else
|
||||
|
||||
/*
|
||||
@@ -0,0 +1,65 @@
|
||||
--- a/libarchive/archive_write_add_filter_gzip.c
|
||||
+++ b/libarchive/archive_write_add_filter_gzip.c
|
||||
@@ -59,12 +59,13 @@
|
||||
int compression_level;
|
||||
int timestamp;
|
||||
char *original_filename;
|
||||
+ unsigned char os;
|
||||
#ifdef HAVE_ZLIB_H
|
||||
z_stream stream;
|
||||
int64_t total_in;
|
||||
unsigned char *compressed;
|
||||
size_t compressed_buffer_size;
|
||||
- unsigned long crc;
|
||||
+ uint32_t crc;
|
||||
#else
|
||||
struct archive_write_program_data *pdata;
|
||||
#endif
|
||||
@@ -108,6 +109,7 @@
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
f->data = data;
|
||||
+ data->os = 3; /* default Unix */
|
||||
f->open = &archive_compressor_gzip_open;
|
||||
f->options = &archive_compressor_gzip_options;
|
||||
f->close = &archive_compressor_gzip_close;
|
||||
@@ -180,6 +182,30 @@
|
||||
if (data->original_filename == NULL)
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
+ return (ARCHIVE_OK);
|
||||
+ }
|
||||
+
|
||||
+ if (strcmp(key, "os") == 0) {
|
||||
+ if (value == NULL)
|
||||
+ return (ARCHIVE_WARN);
|
||||
+
|
||||
+ if (strcmp(value, "FAT") == 0) data->os = 0;
|
||||
+ else if (strcmp(value, "Amiga") == 0) data->os = 1;
|
||||
+ else if (strcmp(value, "VMS") == 0 || strcmp(value, "OpenVMS") == 0) data->os = 2;
|
||||
+ else if (strcmp(value, "Unix") == 0) data->os = 3;
|
||||
+ else if (strcmp(value, "VM") == 0 || strcmp(value, "VM/CMS") == 0) data->os = 4;
|
||||
+ else if (strcmp(value, "Atari TOS") == 0) data->os = 5;
|
||||
+ else if (strcmp(value, "HPFS") == 0) data->os = 6;
|
||||
+ else if (strcmp(value, "Macintosh") == 0) data->os = 7;
|
||||
+ else if (strcmp(value, "Z-System") == 0) data->os = 8;
|
||||
+ else if (strcmp(value, "CP/M") == 0) data->os = 9;
|
||||
+ else if (strcmp(value, "TOPS-20") == 0) data->os = 10;
|
||||
+ else if (strcmp(value, "NTFS") == 0) data->os = 11;
|
||||
+ else if (strcmp(value, "QDOS") == 0) data->os = 12;
|
||||
+ else if (strcmp(value, "Acorn RISCOS") == 0) data->os = 13;
|
||||
+ else if (strcmp(value, "Unknown") == 0) data->os = 255;
|
||||
+ else return (ARCHIVE_WARN);
|
||||
+
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
@@ -245,7 +271,7 @@
|
||||
} else {
|
||||
data->compressed[8] = 0;
|
||||
}
|
||||
- data->compressed[9] = 3; /* OS=Unix */
|
||||
+ data->compressed[9] = data->os;
|
||||
data->stream.next_out += 10;
|
||||
data->stream.avail_out -= 10;
|
||||
|
||||
@@ -0,0 +1,779 @@
|
||||
--- a/libarchive/archive_read_private.h
|
||||
+++ b/libarchive/archive_read_private.h
|
||||
@@ -182,2 +182,14 @@
|
||||
|
||||
+ /*
|
||||
+ * BUN PATCH: set by the format's read_header callback when it
|
||||
+ * returns ARCHIVE_RETRY from a non-blocking source and wants
|
||||
+ * the next call to skip archive_entry_clear() so that
|
||||
+ * attributes already populated by consumed extension headers
|
||||
+ * (pax, GNU long name/link, etc.) survive the resume. Format
|
||||
+ * readers that return ARCHIVE_RETRY without setting this —
|
||||
+ * e.g. upstream tar's damaged-block skip — get the original
|
||||
+ * reset-everything semantics on re-entry.
|
||||
+ */
|
||||
+ int read_header_in_progress;
|
||||
+
|
||||
/* Nodes and offsets of compressed data block */
|
||||
--- a/libarchive/archive_read.c
|
||||
+++ b/libarchive/archive_read.c
|
||||
@@ -616,25 +616,58 @@
|
||||
|
||||
- archive_entry_clear(entry);
|
||||
- archive_clear_error(&a->archive);
|
||||
-
|
||||
/*
|
||||
- * If client didn't consume entire data, skip any remainder
|
||||
- * (This is especially important for GNU incremental directories.)
|
||||
+ * BUN PATCH: when resuming after an ARCHIVE_RETRY from a
|
||||
+ * non-blocking source, the format reader has already
|
||||
+ * populated `entry` from consumed extension headers (pax,
|
||||
+ * GNU long name/link). Clearing it here would discard that
|
||||
+ * work, and since no data has been read for the pending
|
||||
+ * entry there is nothing to skip. Skip straight to the
|
||||
+ * format reader which will pick up where it left off.
|
||||
*/
|
||||
- if (a->archive.state == ARCHIVE_STATE_DATA) {
|
||||
- r1 = archive_read_data_skip(&a->archive);
|
||||
- if (r1 == ARCHIVE_EOF)
|
||||
- archive_set_error(&a->archive, EIO,
|
||||
- "Premature end-of-file");
|
||||
- if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) {
|
||||
- a->archive.state = ARCHIVE_STATE_FATAL;
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ if (!a->read_header_in_progress) {
|
||||
+ archive_entry_clear(entry);
|
||||
+ archive_clear_error(&a->archive);
|
||||
+
|
||||
+ /*
|
||||
+ * If client didn't consume entire data, skip any remainder
|
||||
+ * (This is especially important for GNU incremental directories.)
|
||||
+ */
|
||||
+ if (a->archive.state == ARCHIVE_STATE_DATA) {
|
||||
+ r1 = archive_read_data_skip(&a->archive);
|
||||
+ /*
|
||||
+ * BUN PATCH: the skip may run out of buffered
|
||||
+ * bytes when the source is non-blocking. The
|
||||
+ * format's skip handler has already recorded how
|
||||
+ * much remains, so the next next_header() call
|
||||
+ * will re-enter here (state is still DATA) and
|
||||
+ * continue the skip.
|
||||
+ */
|
||||
+ if (r1 == ARCHIVE_RETRY)
|
||||
+ return (ARCHIVE_RETRY);
|
||||
+ if (r1 == ARCHIVE_EOF)
|
||||
+ archive_set_error(&a->archive, EIO,
|
||||
+ "Premature end-of-file");
|
||||
+ if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) {
|
||||
+ a->archive.state = ARCHIVE_STATE_FATAL;
|
||||
+ return (ARCHIVE_FATAL);
|
||||
+ }
|
||||
}
|
||||
- }
|
||||
|
||||
- /* Record start-of-header offset in uncompressed stream. */
|
||||
- a->header_position = a->filter->position;
|
||||
+ /* Record start-of-header offset in uncompressed stream. */
|
||||
+ a->header_position = a->filter->position;
|
||||
|
||||
- ++_a->file_count;
|
||||
+ ++_a->file_count;
|
||||
+ }
|
||||
r2 = (a->format->read_header)(a, entry);
|
||||
+ /*
|
||||
+ * BUN PATCH: the format reader sets read_header_in_progress
|
||||
+ * itself when a non-blocking source yields mid-header (see
|
||||
+ * `bun_retry` in the tar reader). Clear it on any terminal
|
||||
+ * result so the next call runs the full reset above. An
|
||||
+ * ARCHIVE_RETRY that did not set the flag — upstream tar's
|
||||
+ * pre-existing damaged-block skip — therefore behaves exactly
|
||||
+ * as upstream: the next call re-runs archive_entry_clear /
|
||||
+ * archive_clear_error.
|
||||
+ */
|
||||
+ if (r2 != ARCHIVE_RETRY)
|
||||
+ a->read_header_in_progress = 0;
|
||||
|
||||
@@ -1384,2 +1417,29 @@
|
||||
&filter->client_buff);
|
||||
+ /*
|
||||
+ * BUN PATCH: non-blocking read support.
|
||||
+ *
|
||||
+ * A client reader that is being fed incrementally
|
||||
+ * (e.g. tarball bytes arriving from the network
|
||||
+ * during `bun install`) returns ARCHIVE_RETRY when
|
||||
+ * no data is currently available. Unlike a real
|
||||
+ * error this must not poison the filter: whatever
|
||||
+ * has already been copied into `filter->buffer`
|
||||
+ * stays put so the next call with the same `min`
|
||||
+ * resumes exactly where we left off, and the caller
|
||||
+ * can yield its thread and try again once more
|
||||
+ * input has arrived.
|
||||
+ *
|
||||
+ * Callers that do not opt in (pass avail == NULL or
|
||||
+ * do not check for ARCHIVE_RETRY) will see NULL and
|
||||
+ * treat it as truncated input, which matches the
|
||||
+ * pre-patch behaviour for non-streaming sources.
|
||||
+ */
|
||||
+ if (bytes_read == ARCHIVE_RETRY) {
|
||||
+ filter->client_total = filter->client_avail = 0;
|
||||
+ filter->client_next =
|
||||
+ filter->client_buff = NULL;
|
||||
+ if (avail != NULL)
|
||||
+ *avail = ARCHIVE_RETRY;
|
||||
+ return (NULL);
|
||||
+ }
|
||||
if (bytes_read < 0) { /* Read error. */
|
||||
@@ -1579,3 +1639,10 @@
|
||||
filter->client_buff = NULL;
|
||||
- filter->fatal = 1;
|
||||
+ /*
|
||||
+ * BUN PATCH: see the matching comment in
|
||||
+ * __archive_read_filter_ahead. A non-blocking
|
||||
+ * reader returning ARCHIVE_RETRY must not mark
|
||||
+ * the filter fatal.
|
||||
+ */
|
||||
+ if (bytes_read != ARCHIVE_RETRY)
|
||||
+ filter->fatal = 1;
|
||||
return (bytes_read);
|
||||
--- a/libarchive/archive_read_support_filter_gzip.c
|
||||
+++ b/libarchive/archive_read_support_filter_gzip.c
|
||||
@@ -63,2 +63,9 @@
|
||||
char eof; /* True = found end of compressed data. */
|
||||
+ /*
|
||||
+ * BUN PATCH: set after Z_STREAM_END so that a retry which
|
||||
+ * interrupts consume_trailer() does not re-enter
|
||||
+ * consume_header() (which would try to parse deflate bytes as
|
||||
+ * a gzip header).
|
||||
+ */
|
||||
+ char trailer_pending;
|
||||
};
|
||||
@@ -148,2 +155,10 @@
|
||||
p = __archive_read_filter_ahead(filter, len, &avail);
|
||||
+ /*
|
||||
+ * BUN PATCH: propagate non-blocking retry. Nothing has been
|
||||
+ * consumed yet, so calling peek_at_header again once more
|
||||
+ * input is available will re-read the same header bytes from
|
||||
+ * filter->buffer.
|
||||
+ */
|
||||
+ if (p == NULL && avail == ARCHIVE_RETRY)
|
||||
+ return (ARCHIVE_RETRY);
|
||||
if (p == NULL || avail == 0)
|
||||
@@ -172,3 +187,3 @@
|
||||
if (p == NULL)
|
||||
- return (0);
|
||||
+ return (avail == ARCHIVE_RETRY ? ARCHIVE_RETRY : 0);
|
||||
len += ((int)p[len + 1] << 8) | (int)p[len];
|
||||
@@ -192,3 +207,3 @@
|
||||
if (p == NULL)
|
||||
- return (0);
|
||||
+ return (avail == ARCHIVE_RETRY ? ARCHIVE_RETRY : 0);
|
||||
} while (p[len - 1] != 0);
|
||||
@@ -216,3 +231,3 @@
|
||||
if (p == NULL)
|
||||
- return (0);
|
||||
+ return (avail == ARCHIVE_RETRY ? ARCHIVE_RETRY : 0);
|
||||
} while (p[len - 1] != 0);
|
||||
@@ -224,3 +239,3 @@
|
||||
if (p == NULL)
|
||||
- return (0);
|
||||
+ return (avail == ARCHIVE_RETRY ? ARCHIVE_RETRY : 0);
|
||||
#if 0
|
||||
@@ -251,3 +266,12 @@
|
||||
|
||||
- if (peek_at_header(filter, &bits_checked, NULL))
|
||||
+ /*
|
||||
+ * BUN PATCH: peek_at_header() may now return ARCHIVE_RETRY
|
||||
+ * from a non-blocking source. Bidding has no retry channel,
|
||||
+ * so only accept a positive header length as a match.
|
||||
+ * Streaming callers (bun install) always deliver at least
|
||||
+ * one HTTP chunk — well over the ~10-byte gzip header —
|
||||
+ * before archive_read_open() is invoked, so this branch is
|
||||
+ * defensive rather than expected.
|
||||
+ */
|
||||
+ if (peek_at_header(filter, &bits_checked, NULL) > 0)
|
||||
return (bits_checked);
|
||||
@@ -343,4 +367,3 @@
|
||||
struct private_data *state;
|
||||
- ssize_t avail;
|
||||
- size_t len;
|
||||
+ ssize_t len;
|
||||
int ret;
|
||||
@@ -351,2 +374,10 @@
|
||||
len = peek_at_header(self->upstream, NULL, state);
|
||||
+ /*
|
||||
+ * BUN PATCH: peek_at_header consumes nothing, so on
|
||||
+ * ARCHIVE_RETRY the caller can simply try again later and the
|
||||
+ * partial header bytes already copied into the upstream
|
||||
+ * filter's buffer will still be there.
|
||||
+ */
|
||||
+ if (len == ARCHIVE_RETRY)
|
||||
+ return (ARCHIVE_RETRY);
|
||||
if (len == 0)
|
||||
@@ -358,6 +389,12 @@
|
||||
|
||||
- /* Initialize compression library. */
|
||||
- state->stream.next_in = (unsigned char *)(uintptr_t)
|
||||
- __archive_read_filter_ahead(self->upstream, 1, &avail);
|
||||
- state->stream.avail_in = (uInt)avail;
|
||||
+ /*
|
||||
+ * Initialize compression library. next_in / avail_in are
|
||||
+ * unused by inflateInit2 with a negative windowBits (raw
|
||||
+ * deflate); the main read loop primes them before each
|
||||
+ * inflate() call, so we do not need an extra read-ahead here
|
||||
+ * that could itself return ARCHIVE_RETRY after the header has
|
||||
+ * already been consumed.
|
||||
+ */
|
||||
+ state->stream.next_in = NULL;
|
||||
+ state->stream.avail_in = 0;
|
||||
ret = inflateInit2(&(state->stream),
|
||||
@@ -406,11 +443,21 @@
|
||||
|
||||
- state->in_stream = 0;
|
||||
- switch (inflateEnd(&(state->stream))) {
|
||||
- case Z_OK:
|
||||
- break;
|
||||
- default:
|
||||
- archive_set_error(&self->archive->archive,
|
||||
- ARCHIVE_ERRNO_MISC,
|
||||
- "Failed to clean up gzip decompressor");
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ /*
|
||||
+ * BUN PATCH: inflateEnd()/in_stream=0 are only safe to run
|
||||
+ * once. If a previous consume_trailer() attempt was
|
||||
+ * interrupted by ARCHIVE_RETRY while reading the 8-byte
|
||||
+ * footer, `trailer_pending` stays set and we skip straight to
|
||||
+ * the read-ahead below.
|
||||
+ */
|
||||
+ if (!state->trailer_pending) {
|
||||
+ state->in_stream = 0;
|
||||
+ switch (inflateEnd(&(state->stream))) {
|
||||
+ case Z_OK:
|
||||
+ break;
|
||||
+ default:
|
||||
+ archive_set_error(&self->archive->archive,
|
||||
+ ARCHIVE_ERRNO_MISC,
|
||||
+ "Failed to clean up gzip decompressor");
|
||||
+ return (ARCHIVE_FATAL);
|
||||
+ }
|
||||
+ state->trailer_pending = 1;
|
||||
}
|
||||
@@ -419,2 +466,4 @@
|
||||
p = __archive_read_filter_ahead(self->upstream, 8, &avail);
|
||||
+ if (p == NULL && avail == ARCHIVE_RETRY)
|
||||
+ return (ARCHIVE_RETRY);
|
||||
if (p == NULL || avail == 0)
|
||||
@@ -426,2 +475,3 @@
|
||||
__archive_read_filter_consume(self->upstream, 8);
|
||||
+ state->trailer_pending = 0;
|
||||
|
||||
@@ -446,2 +496,14 @@
|
||||
while (state->stream.avail_out > 0 && !state->eof) {
|
||||
+ /*
|
||||
+ * BUN PATCH: finish any pending trailer first. This can
|
||||
+ * only be set when a previous gzip_filter_read() call hit
|
||||
+ * ARCHIVE_RETRY inside consume_trailer().
|
||||
+ */
|
||||
+ if (state->trailer_pending) {
|
||||
+ ret = consume_trailer(self);
|
||||
+ if (ret == ARCHIVE_RETRY)
|
||||
+ goto bun_retry;
|
||||
+ if (ret < ARCHIVE_OK)
|
||||
+ return (ret);
|
||||
+ }
|
||||
/* If we're not in a stream, read a header
|
||||
@@ -454,2 +516,4 @@
|
||||
}
|
||||
+ if (ret == ARCHIVE_RETRY)
|
||||
+ goto bun_retry;
|
||||
if (ret < ARCHIVE_OK)
|
||||
@@ -464,2 +528,10 @@
|
||||
if (state->stream.next_in == NULL) {
|
||||
+ /*
|
||||
+ * BUN PATCH: upstream has no bytes right now but
|
||||
+ * isn't done. zlib's inflate state is untouched,
|
||||
+ * so propagate the retry (or return whatever we
|
||||
+ * have already decompressed this call).
|
||||
+ */
|
||||
+ if (avail_in == ARCHIVE_RETRY)
|
||||
+ goto bun_retry;
|
||||
archive_set_error(&self->archive->archive,
|
||||
@@ -490,2 +562,4 @@
|
||||
ret = consume_trailer(self);
|
||||
+ if (ret == ARCHIVE_RETRY)
|
||||
+ goto bun_retry;
|
||||
if (ret < ARCHIVE_OK)
|
||||
@@ -510,2 +584,21 @@
|
||||
return (decompressed);
|
||||
+
|
||||
+bun_retry:
|
||||
+ /*
|
||||
+ * BUN PATCH: upstream ran dry mid-call. If we managed to
|
||||
+ * decompress anything before that happened, hand it back
|
||||
+ * now; the tar layer will consume it and the next call will
|
||||
+ * start with an empty out_block and immediately retry.
|
||||
+ * Otherwise propagate ARCHIVE_RETRY so the tar layer (and
|
||||
+ * ultimately the Zig extract loop) can yield this worker and
|
||||
+ * reschedule when more bytes arrive.
|
||||
+ */
|
||||
+ decompressed = state->stream.next_out - state->out_block;
|
||||
+ if (decompressed > 0) {
|
||||
+ state->total_out += decompressed;
|
||||
+ *p = state->out_block;
|
||||
+ return (decompressed);
|
||||
+ }
|
||||
+ *p = NULL;
|
||||
+ return (ARCHIVE_RETRY);
|
||||
}
|
||||
--- a/libarchive/archive_read_support_format_tar.c
|
||||
+++ b/libarchive/archive_read_support_format_tar.c
|
||||
@@ -155,2 +155,19 @@
|
||||
int read_concatenated_archives;
|
||||
+
|
||||
+ /*
|
||||
+ * BUN PATCH: resume state for non-blocking sources.
|
||||
+ *
|
||||
+ * `tar_read_header` walks a variable-length sequence of
|
||||
+ * extension headers (pax 'x'/'g', GNU 'L'/'K', etc.) before
|
||||
+ * the real ustar header. When the underlying reader returns
|
||||
+ * ARCHIVE_RETRY part-way through that walk, we must remember
|
||||
+ * which extensions have already been parsed so the next call
|
||||
+ * picks up at the right header instead of re-running
|
||||
+ * `tar_reset_header_state` and re-bidding bytes that were
|
||||
+ * already consumed.
|
||||
+ */
|
||||
+ int header_in_progress;
|
||||
+ int32_t header_seen;
|
||||
+ int header_eof_fatal;
|
||||
+ int header_err;
|
||||
};
|
||||
@@ -231,2 +248,4 @@
|
||||
static int64_t tar_atol8(const char *, size_t);
|
||||
+static int tar_consume_retryable(struct archive_read *, struct tar *,
|
||||
+ int64_t);
|
||||
static int tar_read_header(struct archive_read *, struct tar *,
|
||||
@@ -475,2 +494,49 @@
|
||||
|
||||
+/*
|
||||
+ * BUN PATCH: consume up to `request` bytes in a way that is safe to
|
||||
+ * resume from a non-blocking source. Each iteration uses
|
||||
+ * __archive_read_ahead(1) to discover how many bytes are currently
|
||||
+ * buffered and then __archive_read_consume()s exactly that much, so
|
||||
+ * the consume never has to invoke the client reader itself. On
|
||||
+ * ARCHIVE_RETRY the remaining amount is written back into
|
||||
+ * `tar->entry_bytes_remaining` / `tar->entry_padding` so the next
|
||||
+ * call (via archive_read_next_header → read_data_skip, or another
|
||||
+ * read_data_block) continues from the right offset.
|
||||
+ */
|
||||
+static int
|
||||
+tar_consume_retryable(struct archive_read *a, struct tar *tar,
|
||||
+ int64_t request)
|
||||
+{
|
||||
+ while (request > 0) {
|
||||
+ ssize_t avail = 0;
|
||||
+ const void *p = __archive_read_ahead(a, 1, &avail);
|
||||
+ if (p == NULL) {
|
||||
+ if (avail == ARCHIVE_RETRY) {
|
||||
+ /* Persist what is still owed so the
|
||||
+ * next resume asks for the remainder
|
||||
+ * rather than the original total. */
|
||||
+ if (request >= tar->entry_padding) {
|
||||
+ tar->entry_bytes_remaining =
|
||||
+ request - tar->entry_padding;
|
||||
+ } else {
|
||||
+ tar->entry_bytes_remaining = 0;
|
||||
+ tar->entry_padding = request;
|
||||
+ }
|
||||
+ return (ARCHIVE_RETRY);
|
||||
+ }
|
||||
+ archive_set_error(&a->archive,
|
||||
+ ARCHIVE_ERRNO_MISC,
|
||||
+ "Truncated tar archive"
|
||||
+ " detected while skipping data");
|
||||
+ return (ARCHIVE_FATAL);
|
||||
+ }
|
||||
+ if (avail > request)
|
||||
+ avail = (ssize_t)request;
|
||||
+ if (__archive_read_consume(a, avail) != avail)
|
||||
+ return (ARCHIVE_FATAL);
|
||||
+ request -= avail;
|
||||
+ }
|
||||
+ return (ARCHIVE_OK);
|
||||
+}
|
||||
+
|
||||
/* utility function- this exists to centralize the logic of tracking
|
||||
@@ -535,25 +601,35 @@
|
||||
|
||||
- /* Assign default device/inode values. */
|
||||
- archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */
|
||||
- archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */
|
||||
- /* Limit generated st_ino number to 16 bits. */
|
||||
- if (default_inode >= 0xffff) {
|
||||
- ++default_dev;
|
||||
- default_inode = 0;
|
||||
- }
|
||||
-
|
||||
tar = (struct tar *)(a->format->data);
|
||||
- tar->entry_offset = 0;
|
||||
- gnu_clear_sparse_list(tar);
|
||||
- tar->size_fields = 0; /* We don't have any size info yet */
|
||||
|
||||
- /* Setup default string conversion. */
|
||||
- tar->sconv = tar->opt_sconv;
|
||||
- if (tar->sconv == NULL) {
|
||||
- if (!tar->init_default_conversion) {
|
||||
- tar->sconv_default =
|
||||
- archive_string_default_conversion_for_read(&(a->archive));
|
||||
- tar->init_default_conversion = 1;
|
||||
+ /*
|
||||
+ * BUN PATCH: when resuming after an ARCHIVE_RETRY from a
|
||||
+ * non-blocking source, skip the fresh-entry initialisation
|
||||
+ * below. Extension headers parsed before the retry have
|
||||
+ * already written into `entry` and `tar->entry_*`; repeating
|
||||
+ * these resets would discard that work.
|
||||
+ */
|
||||
+ if (!tar->header_in_progress) {
|
||||
+ /* Assign default device/inode values. */
|
||||
+ archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */
|
||||
+ archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */
|
||||
+ /* Limit generated st_ino number to 16 bits. */
|
||||
+ if (default_inode >= 0xffff) {
|
||||
+ ++default_dev;
|
||||
+ default_inode = 0;
|
||||
+ }
|
||||
+
|
||||
+ tar->entry_offset = 0;
|
||||
+ gnu_clear_sparse_list(tar);
|
||||
+ tar->size_fields = 0; /* We don't have any size info yet */
|
||||
+
|
||||
+ /* Setup default string conversion. */
|
||||
+ tar->sconv = tar->opt_sconv;
|
||||
+ if (tar->sconv == NULL) {
|
||||
+ if (!tar->init_default_conversion) {
|
||||
+ tar->sconv_default =
|
||||
+ archive_string_default_conversion_for_read(&(a->archive));
|
||||
+ tar->init_default_conversion = 1;
|
||||
+ }
|
||||
+ tar->sconv = tar->sconv_default;
|
||||
}
|
||||
- tar->sconv = tar->sconv_default;
|
||||
}
|
||||
@@ -562,2 +638,13 @@
|
||||
|
||||
+ /*
|
||||
+ * BUN PATCH: a non-blocking yield leaves `header_in_progress`
|
||||
+ * set; in that case `*unconsumed == 0` (nothing pending that
|
||||
+ * isn't already flushed) so the caller can yield and re-enter
|
||||
+ * later. Upstream's damaged-block ARCHIVE_RETRY clears the
|
||||
+ * flag and falls through to the original post-read handling
|
||||
+ * below (which is what upstream did).
|
||||
+ */
|
||||
+ if (r == ARCHIVE_RETRY && tar->header_in_progress)
|
||||
+ return (ARCHIVE_RETRY);
|
||||
+
|
||||
tar_flush_unconsumed(a, &unconsumed);
|
||||
@@ -637,5 +724,16 @@
|
||||
|
||||
- if (__archive_read_consume(a, request) != request)
|
||||
+ /*
|
||||
+ * BUN PATCH: make the end-of-entry padding
|
||||
+ * consume resumable. `tar_consume_retryable`
|
||||
+ * decrements `entry_bytes_remaining` /
|
||||
+ * `entry_padding` as bytes become available so a
|
||||
+ * subsequent call picks up with the remainder.
|
||||
+ */
|
||||
+ int cr = tar_consume_retryable(a, tar, request);
|
||||
+ if (cr == ARCHIVE_RETRY)
|
||||
+ return (ARCHIVE_RETRY);
|
||||
+ if (cr != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
tar->entry_padding = 0;
|
||||
+ tar->entry_bytes_remaining = 0;
|
||||
*buff = NULL;
|
||||
@@ -648,2 +746,11 @@
|
||||
if (*buff == NULL) {
|
||||
+ /*
|
||||
+ * BUN PATCH: propagate non-blocking retry. Every
|
||||
+ * counter we would have touched
|
||||
+ * (`entry_bytes_remaining`, `sparse_list`, etc.)
|
||||
+ * is still at its pre-call value, so the caller
|
||||
+ * can yield and re-enter read_data_block later.
|
||||
+ */
|
||||
+ if (bytes_read == ARCHIVE_RETRY)
|
||||
+ return (ARCHIVE_RETRY);
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
@@ -676,2 +783,3 @@
|
||||
int64_t request;
|
||||
+ int cr;
|
||||
struct tar* tar;
|
||||
@@ -683,3 +791,14 @@
|
||||
|
||||
- if (__archive_read_consume(a, request) != request)
|
||||
+ /*
|
||||
+ * BUN PATCH: use the retryable consume so a non-blocking
|
||||
+ * source can yield mid-skip. Progress is recorded in
|
||||
+ * `entry_bytes_remaining` / `entry_padding` /
|
||||
+ * `entry_bytes_unconsumed`, so archive_read_next_header's
|
||||
+ * implicit skip on re-entry resumes with the remainder.
|
||||
+ */
|
||||
+ tar->entry_bytes_unconsumed = 0;
|
||||
+ cr = tar_consume_retryable(a, tar, request);
|
||||
+ if (cr == ARCHIVE_RETRY)
|
||||
+ return (ARCHIVE_RETRY);
|
||||
+ if (cr != ARCHIVE_OK)
|
||||
return (ARCHIVE_FATAL);
|
||||
@@ -687,3 +806,2 @@
|
||||
tar->entry_bytes_remaining = 0;
|
||||
- tar->entry_bytes_unconsumed = 0;
|
||||
tar->entry_padding = 0;
|
||||
@@ -721,4 +839,4 @@
|
||||
ssize_t bytes;
|
||||
- int err = ARCHIVE_OK, err2;
|
||||
- int eof_fatal = 0; /* EOF is okay at some points... */
|
||||
+ int err, err2;
|
||||
+ int eof_fatal; /* EOF is okay at some points... */
|
||||
const char *h;
|
||||
@@ -728,3 +846,3 @@
|
||||
/* Bitmask of what header types we've seen. */
|
||||
- int32_t seen_headers = 0;
|
||||
+ int32_t seen_headers;
|
||||
static const int32_t seen_A_header = 1;
|
||||
@@ -737,3 +855,20 @@
|
||||
|
||||
- tar_reset_header_state(tar);
|
||||
+ /*
|
||||
+ * BUN PATCH: `header_in_progress` persists the header loop's
|
||||
+ * locals across an ARCHIVE_RETRY so a non-blocking source can
|
||||
+ * resume mid-sequence. On a fresh entry we initialise them
|
||||
+ * exactly as before; on resume we restore and skip the reset
|
||||
+ * that would otherwise wipe pax/GNU long-name data already
|
||||
+ * parsed into `tar->entry_*`.
|
||||
+ */
|
||||
+ if (!tar->header_in_progress) {
|
||||
+ tar_reset_header_state(tar);
|
||||
+ tar->header_in_progress = 1;
|
||||
+ tar->header_seen = 0;
|
||||
+ tar->header_eof_fatal = 0;
|
||||
+ tar->header_err = ARCHIVE_OK;
|
||||
+ }
|
||||
+ seen_headers = tar->header_seen;
|
||||
+ eof_fatal = tar->header_eof_fatal;
|
||||
+ err = tar->header_err;
|
||||
|
||||
@@ -745,2 +880,13 @@
|
||||
|
||||
+/*
|
||||
+ * BUN PATCH: every terminal exit from this function (anything other
|
||||
+ * than the non-blocking retry path) must clear `header_in_progress`
|
||||
+ * so the next entry starts with fresh `seen_headers` / header state.
|
||||
+ * The macro keeps the existing `return (X)` sites readable.
|
||||
+ */
|
||||
+#define TAR_HEADER_RETURN(rc) do { \
|
||||
+ tar->header_in_progress = 0; \
|
||||
+ return (rc); \
|
||||
+ } while (0)
|
||||
+
|
||||
/*
|
||||
@@ -757,3 +903,3 @@
|
||||
if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
}
|
||||
@@ -762,2 +908,13 @@
|
||||
h = __archive_read_ahead(a, 512, &bytes);
|
||||
+ if (h == NULL && bytes == ARCHIVE_RETRY) {
|
||||
+ /*
|
||||
+ * BUN PATCH: non-blocking source ran
|
||||
+ * out mid-header. Nothing has been
|
||||
+ * added to `*unconsumed` yet this
|
||||
+ * iteration, so save loop state and
|
||||
+ * let the caller try again once more
|
||||
+ * input arrives.
|
||||
+ */
|
||||
+ goto bun_retry;
|
||||
+ }
|
||||
if (bytes == 0) { /* EOF at a block boundary. */
|
||||
@@ -769,5 +926,5 @@
|
||||
"Damaged tar archive (end-of-archive within a sequence of headers)");
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
} else {
|
||||
- return (ARCHIVE_EOF);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_EOF);
|
||||
}
|
||||
@@ -779,3 +936,3 @@
|
||||
" detected while reading next header");
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
}
|
||||
@@ -799,3 +956,3 @@
|
||||
archive_clear_error(&a->archive);
|
||||
- return (ARCHIVE_EOF);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_EOF);
|
||||
}
|
||||
@@ -805,3 +962,3 @@
|
||||
if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
}
|
||||
@@ -812,5 +969,18 @@
|
||||
if (eof_fatal) {
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
} else {
|
||||
- return (ARCHIVE_RETRY);
|
||||
+ /*
|
||||
+ * BUN PATCH: upstream's
|
||||
+ * "damaged block, skip and retry
|
||||
+ * the next one" — NOT a
|
||||
+ * non-blocking yield. The next
|
||||
+ * call must re-run the full
|
||||
+ * state reset (archive_entry_clear,
|
||||
+ * tar_reset_header_state) exactly
|
||||
+ * as upstream, so clear both
|
||||
+ * in-progress flags instead of
|
||||
+ * routing through bun_retry.
|
||||
+ */
|
||||
+ a->read_header_in_progress = 0;
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_RETRY);
|
||||
}
|
||||
@@ -822,2 +992,63 @@
|
||||
header = (const struct archive_entry_header_ustar *)h;
|
||||
+
|
||||
+ /*
|
||||
+ * BUN PATCH: extension headers ('A','g','K','L','V',
|
||||
+ * 'x','X') are followed by a payload whose length is
|
||||
+ * encoded in this block's `size` field. The handlers
|
||||
+ * below flush `*unconsumed` (this 512-byte block) and
|
||||
+ * then __archive_read_ahead() the payload; if that
|
||||
+ * ahead were to return ARCHIVE_RETRY after the flush,
|
||||
+ * the block would already be consumed and the resume
|
||||
+ * would read payload bytes as a header.
|
||||
+ *
|
||||
+ * To keep the handlers unchanged, pre-buffer the
|
||||
+ * header+payload here *before* the flush. On retry we
|
||||
+ * roll back `*unconsumed` so nothing is consumed and
|
||||
+ * the next attempt re-reads this exact block from the
|
||||
+ * filter's buffer.
|
||||
+ */
|
||||
+ switch (header->typeflag[0]) {
|
||||
+ case 'A': case 'g': case 'K': case 'L':
|
||||
+ case 'V': case 'X': case 'x': {
|
||||
+ int64_t ext_size = tar_atol(header->size,
|
||||
+ sizeof(header->size));
|
||||
+ /* Bound the prebuffer before rounding
|
||||
+ * (tar_atol saturates to INT64_MAX on
|
||||
+ * overflow, so adding 511 first would be UB).
|
||||
+ * A corrupted stream can produce garbage that
|
||||
+ * decodes as an extension header with a
|
||||
+ * multi-GB `size`; retrying `ahead(512+huge)`
|
||||
+ * every chunk would never make progress. Real
|
||||
+ * pax/GNU extension payloads are path-length
|
||||
+ * bounded (a few KB), so anything beyond this
|
||||
+ * is handed to the handler below, which will
|
||||
+ * fail cleanly on its own
|
||||
+ * `__archive_read_ahead`. */
|
||||
+ if (ext_size < 0 || ext_size > (int64_t)(1u << 20))
|
||||
+ break;
|
||||
+ int64_t ext_padded = (ext_size + 511) & ~511;
|
||||
+ if (__archive_read_ahead(a,
|
||||
+ (size_t)(512 + ext_padded), &bytes) == NULL &&
|
||||
+ bytes == ARCHIVE_RETRY) {
|
||||
+ *unconsumed -= 512;
|
||||
+ goto bun_retry;
|
||||
+ }
|
||||
+ /* Re-establish `h`: the filter may have
|
||||
+ * memmoved its buffer to satisfy the larger
|
||||
+ * request. A fatal error from the larger
|
||||
+ * read (alloc failure, downstream I/O error)
|
||||
+ * leaves `filter->fatal` set, so this read
|
||||
+ * can return NULL even though the original
|
||||
+ * 512 bytes are still buffered; bail cleanly
|
||||
+ * rather than dereferencing `header` below. */
|
||||
+ h = __archive_read_ahead(a, 512, NULL);
|
||||
+ if (h == NULL)
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
+ header = (const struct archive_entry_header_ustar *)h;
|
||||
+ break;
|
||||
+ }
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
switch(header->typeflag[0]) {
|
||||
@@ -827,3 +1058,3 @@
|
||||
"Redundant 'A' header");
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
}
|
||||
@@ -838,3 +1069,3 @@
|
||||
"Redundant 'g' header");
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
}
|
||||
@@ -882,3 +1113,3 @@
|
||||
"Redundant 'X'/'x' header");
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
}
|
||||
@@ -894,3 +1125,3 @@
|
||||
"Redundant 'x' header");
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
}
|
||||
@@ -921,3 +1152,3 @@
|
||||
if (err < ARCHIVE_WARN) {
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
}
|
||||
@@ -940,3 +1171,3 @@
|
||||
if (err2 < ARCHIVE_WARN) {
|
||||
- return (ARCHIVE_FATAL);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_FATAL);
|
||||
}
|
||||
@@ -955,3 +1186,3 @@
|
||||
"Non-regular file cannot be sparse");
|
||||
- return (ARCHIVE_WARN);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_WARN);
|
||||
} else if (tar->sparse_gnu_major == 0 &&
|
||||
@@ -968,3 +1199,3 @@
|
||||
if (bytes_read < 0)
|
||||
- return ((int)bytes_read);
|
||||
+ TAR_HEADER_RETURN((int)bytes_read);
|
||||
tar->entry_bytes_remaining -= bytes_read;
|
||||
@@ -974,6 +1205,6 @@
|
||||
"Unrecognized GNU sparse file format");
|
||||
- return (ARCHIVE_WARN);
|
||||
+ TAR_HEADER_RETURN(ARCHIVE_WARN);
|
||||
}
|
||||
}
|
||||
- return (err);
|
||||
+ TAR_HEADER_RETURN(err);
|
||||
}
|
||||
@@ -983,3 +1214,3 @@
|
||||
if (err == ARCHIVE_FATAL)
|
||||
- return (err);
|
||||
+ TAR_HEADER_RETURN(err);
|
||||
|
||||
@@ -993,2 +1224,22 @@
|
||||
}
|
||||
+
|
||||
+bun_retry:
|
||||
+ /*
|
||||
+ * BUN PATCH: persist loop state so the next tar_read_header()
|
||||
+ * call resumes exactly here. At this point `*unconsumed == 0`
|
||||
+ * (either nothing was added this iteration, or we rolled the
|
||||
+ * 512-byte header back above), so
|
||||
+ * `archive_read_format_tar_read_header` can return
|
||||
+ * ARCHIVE_RETRY without flushing. Setting
|
||||
+ * `read_header_in_progress` here (rather than having
|
||||
+ * archive_read_next_header2 set it on every RETRY) is what
|
||||
+ * lets upstream's damaged-block RETRY keep its original
|
||||
+ * reset-everything semantics.
|
||||
+ */
|
||||
+ tar->header_seen = seen_headers;
|
||||
+ tar->header_eof_fatal = eof_fatal;
|
||||
+ tar->header_err = err;
|
||||
+ a->read_header_in_progress = 1;
|
||||
+ return (ARCHIVE_RETRY);
|
||||
+#undef TAR_HEADER_RETURN
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
--- a/src/turbojpeg.c
|
||||
+++ b/src/turbojpeg.c
|
||||
@@ -1220,12 +1220,14 @@
|
||||
#define BITS_IN_JSAMPLE 8
|
||||
#include "turbojpeg-mp.c"
|
||||
#undef BITS_IN_JSAMPLE
|
||||
+#ifndef BUN_8BIT_ONLY
|
||||
#define BITS_IN_JSAMPLE 12
|
||||
#include "turbojpeg-mp.c"
|
||||
#undef BITS_IN_JSAMPLE
|
||||
#define BITS_IN_JSAMPLE 16
|
||||
#include "turbojpeg-mp.c"
|
||||
#undef BITS_IN_JSAMPLE
|
||||
+#endif
|
||||
|
||||
/* TurboJPEG 1.2+ */
|
||||
DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
|
||||
--- a/src/turbojpeg-mp.c
|
||||
+++ b/src/turbojpeg-mp.c
|
||||
@@ -280,6 +280,7 @@
|
||||
|
||||
/*************************** Packed-Pixel Image I/O **************************/
|
||||
|
||||
+#ifndef BUN_8BIT_ONLY
|
||||
#if BITS_IN_JSAMPLE != 16 || defined(C_LOSSLESS_SUPPORTED)
|
||||
|
||||
/* TurboJPEG 3.0+ */
|
||||
@@ -561,6 +562,7 @@
|
||||
|
||||
#endif
|
||||
}
|
||||
+#endif /* BUN_8BIT_ONLY */
|
||||
|
||||
|
||||
#undef _JSAMPLE
|
||||
@@ -0,0 +1,64 @@
|
||||
/* libjpeg-turbo's master_selection() in jcmaster.c/jdmaster.c dispatches at
|
||||
* RUNTIME on cinfo->data_precision and calls j12init_* / j16init_* directly,
|
||||
* with no compile-time gate. Upstream satisfies those by recompiling a subset
|
||||
* of sources twice more with -DBITS_IN_JSAMPLE=12/16; we only ship 8-bit, so
|
||||
* provide ERREXIT stubs instead and avoid ~60 extra TUs. The branches that
|
||||
* reach these are already preceded by a precision check that ERREXITs for
|
||||
* anything Bun.Image would feed in, so these stubs are belt-and-braces.
|
||||
*
|
||||
* tj3LoadImage8/tj3SaveImage8 are gated out by 8bit-only.patch but the legacy
|
||||
* v2 tjLoadImage/tjSaveImage shims still reference them; stub those too.
|
||||
*/
|
||||
|
||||
#define JPEG_INTERNALS
|
||||
#include "jinclude.h"
|
||||
#include "jpeglib.h"
|
||||
#include "jerror.h"
|
||||
|
||||
#define DIE_C(name) \
|
||||
void name(j_compress_ptr cinfo) { ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); }
|
||||
#define DIE_C2(name) \
|
||||
void name(j_compress_ptr cinfo, boolean b) { (void)b; ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); }
|
||||
#define DIE_D(name) \
|
||||
void name(j_decompress_ptr cinfo) { ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); }
|
||||
#define DIE_D2(name) \
|
||||
void name(j_decompress_ptr cinfo, boolean b) { (void)b; ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); }
|
||||
|
||||
DIE_C2(j12init_c_coef_controller)
|
||||
DIE_C2(j12init_c_diff_controller)
|
||||
DIE_C2(j12init_c_main_controller)
|
||||
DIE_C2(j12init_c_prep_controller)
|
||||
DIE_C(j12init_color_converter)
|
||||
DIE_C(j12init_downsampler)
|
||||
DIE_C(j12init_forward_dct)
|
||||
DIE_C(j12init_lossless_compressor)
|
||||
DIE_C2(j16init_c_diff_controller)
|
||||
DIE_C2(j16init_c_main_controller)
|
||||
DIE_C2(j16init_c_prep_controller)
|
||||
DIE_C(j16init_color_converter)
|
||||
DIE_C(j16init_downsampler)
|
||||
DIE_C(j16init_lossless_compressor)
|
||||
|
||||
DIE_D(j12init_color_deconverter)
|
||||
DIE_D2(j12init_d_coef_controller)
|
||||
DIE_D2(j12init_d_diff_controller)
|
||||
DIE_D2(j12init_d_main_controller)
|
||||
DIE_D2(j12init_d_post_controller)
|
||||
DIE_D(j12init_inverse_dct)
|
||||
DIE_D(j12init_lossless_decompressor)
|
||||
DIE_D(j12init_merged_upsampler)
|
||||
DIE_D(j12init_upsampler)
|
||||
DIE_D(j12init_1pass_quantizer)
|
||||
DIE_D(j12init_2pass_quantizer)
|
||||
DIE_D(j16init_color_deconverter)
|
||||
DIE_D2(j16init_d_diff_controller)
|
||||
DIE_D2(j16init_d_main_controller)
|
||||
DIE_D2(j16init_d_post_controller)
|
||||
DIE_D(j16init_lossless_decompressor)
|
||||
DIE_D(j16init_merged_upsampler)
|
||||
DIE_D(j16init_upsampler)
|
||||
|
||||
unsigned char *tj3LoadImage8(void *h, const char *f, int *w, int a, int *ht, int *pf)
|
||||
{ (void)h; (void)f; (void)w; (void)a; (void)ht; (void)pf; return 0; }
|
||||
int tj3SaveImage8(void *h, const char *f, const unsigned char *b, int w, int p, int ht, int pf)
|
||||
{ (void)h; (void)f; (void)b; (void)w; (void)p; (void)ht; (void)pf; return -1; }
|
||||
@@ -0,0 +1,51 @@
|
||||
--- a/src/win/poll.c
|
||||
+++ b/src/win/poll.c
|
||||
@@ -115,9 +115,24 @@
|
||||
AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT;
|
||||
} else {
|
||||
if (handle->events & UV_DISCONNECT) {
|
||||
- afd_poll_info->Handles[0].Events |= AFD_POLL_DISCONNECT;
|
||||
+ /* A peer abort (RST) must wake a watcher that asked for disconnect
|
||||
+ * even when it is not reading: AFD only reports subscribed events, so
|
||||
+ * without ABORT here a reset against a write-only poll is never
|
||||
+ * delivered and teardown waits forever (see the reporting hunk). */
|
||||
+ afd_poll_info->Handles[0].Events |= AFD_POLL_DISCONNECT |
|
||||
+ AFD_POLL_ABORT;
|
||||
}
|
||||
}
|
||||
+ if (handle->events & UV_PRIORITIZED) {
|
||||
+ /* ABORT-only subscription. A graceful FIN leaves AFD_POLL_DISCONNECT
|
||||
+ * signaled forever (AFD is level-triggered), so a watcher that already
|
||||
+ * consumed the FIN cannot keep DISCONNECT armed without completing
|
||||
+ * instantly in a loop - yet it still needs the peer's later RST.
|
||||
+ * UV_PRIORITIZED, unused on Windows, subscribes exactly that remaining
|
||||
+ * signal; the reporting below surfaces it as UV_PRIORITIZED so the
|
||||
+ * requested-events mask keeps it. */
|
||||
+ afd_poll_info->Handles[0].Events |= AFD_POLL_ABORT;
|
||||
+ }
|
||||
if (handle->events & UV_WRITABLE) {
|
||||
afd_poll_info->Handles[0].Events |= AFD_POLL_SEND | AFD_POLL_CONNECT_FAIL;
|
||||
}
|
||||
@@ -168,9 +183,19 @@
|
||||
if ((afd_poll_info->Handles[0].Events & (AFD_POLL_RECEIVE |
|
||||
AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_ABORT)) != 0) {
|
||||
events |= UV_READABLE;
|
||||
- if ((afd_poll_info->Handles[0].Events & AFD_POLL_DISCONNECT) != 0) {
|
||||
- events |= UV_DISCONNECT;
|
||||
- }
|
||||
+ }
|
||||
+ if ((afd_poll_info->Handles[0].Events &
|
||||
+ (AFD_POLL_DISCONNECT | AFD_POLL_ABORT)) != 0) {
|
||||
+ /* Graceful FIN and abortive RST both mean the peer is gone. Report
|
||||
+ * DISCONNECT for either, outside the READABLE block, so it survives
|
||||
+ * the requested-events mask when only UV_DISCONNECT is armed. */
|
||||
+ events |= UV_DISCONNECT;
|
||||
+ }
|
||||
+ if ((afd_poll_info->Handles[0].Events & AFD_POLL_ABORT) != 0) {
|
||||
+ /* The ABORT-only subscription reports as UV_PRIORITIZED: a watcher
|
||||
+ * armed with UV_PRIORITIZED alone has UV_DISCONNECT stripped by the
|
||||
+ * requested-events mask below. */
|
||||
+ events |= UV_PRIORITIZED;
|
||||
}
|
||||
if ((afd_poll_info->Handles[0].Events & (AFD_POLL_SEND |
|
||||
AFD_POLL_CONNECT_FAIL)) != 0) {
|
||||
@@ -0,0 +1,36 @@
|
||||
--- a/src/win/poll.c
|
||||
+++ b/src/win/poll.c
|
||||
@@ -186,11 +186,33 @@
|
||||
uv__handle_stop(handle);
|
||||
}
|
||||
|
||||
+ /* Re-arm the AFD poll **before** invoking poll_cb so an AFD ioctl is
|
||||
+ * pending while user code runs. AFD is level-triggered (ReactOS
|
||||
+ * AfdSelect: `Events & FCB->PollState` checked on IRP arrival), so a
|
||||
+ * RST that lands during poll_cb is caught by the freshly-submitted req.
|
||||
+ * Re-arming *after* poll_cb (the previous order) leaves a gap: an
|
||||
+ * in-process loopback peer that RSTs from inside the callback —
|
||||
+ * e.g. a JS handler that drainMicrotasks() into a same-process
|
||||
+ * fetch().abort() — can land between completion and re-submission,
|
||||
+ * and although AFD_POLL_ABORT is in PollState, no ioctl is pending
|
||||
+ * to observe it until after the callback returns. wepoll does the
|
||||
+ * same re-arm-before-return (port_wait → port__update_events_if_polling
|
||||
+ * before LeaveCriticalSection). The dual-req + mask_events bookkeeping
|
||||
+ * already handles poll_cb calling uv_poll_start/stop mid-flight. */
|
||||
+ if ((handle->events & ~(handle->submitted_events_1 |
|
||||
+ handle->submitted_events_2)) != 0) {
|
||||
+ uv__fast_poll_submit_poll_req(loop, handle);
|
||||
+ }
|
||||
+
|
||||
if (events != 0) {
|
||||
handle->poll_cb(handle, 0, events);
|
||||
}
|
||||
}
|
||||
|
||||
+ /* poll_cb may have called uv_poll_start (covered above by the dual-req
|
||||
+ * submitting the other slot) or uv_poll_stop / uv_close. Only the
|
||||
+ * close-endgame check needs to run post-callback. A re-submit here is
|
||||
+ * still needed for the `events == 0` path (filtered-out completion). */
|
||||
if ((handle->events & ~(handle->submitted_events_1 |
|
||||
handle->submitted_events_2)) != 0) {
|
||||
uv__fast_poll_submit_poll_req(loop, handle);
|
||||
@@ -0,0 +1,146 @@
|
||||
--- a/lshpack.c 2026-05-02 06:41:46.290110113 +0000
|
||||
+++ b/lshpack.c 2026-05-02 06:42:07.599902350 +0000
|
||||
@@ -53,6 +53,103 @@
|
||||
|
||||
#include "huff-tables.h"
|
||||
|
||||
+#if LS_HPACK_USE_LARGE_TABLES && LS_HPACK_BSS_LARGE_TABLES
|
||||
+/* Bun: hencs[65536] (512 KB) and hdecs[65536] (256 KB) are pure functions of
|
||||
+ * encode_table[257]. Declare them in .bss instead of .rodata and fill once on
|
||||
+ * first lshpack/lsqpack init — ~250 us, identical hot-path lookup afterwards.
|
||||
+ * Non-static so lsqpack.c can extern the same arrays instead of carrying its
|
||||
+ * own copy. */
|
||||
+struct henc { unsigned lens; uint32_t code; };
|
||||
+struct hdec { uint8_t lens; uint8_t out[3]; };
|
||||
+struct henc hencs[65536];
|
||||
+struct hdec hdecs[65536];
|
||||
+static int hpack_huff_tables_ready;
|
||||
+
|
||||
+#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
+#define HENC_I(i,j) (((j)<<8)|(i))
|
||||
+#else
|
||||
+#define HENC_I(i,j) (((i)<<8)|(j))
|
||||
+#endif
|
||||
+
|
||||
+void
|
||||
+lshpack_huff_tables_init (void)
|
||||
+{
|
||||
+ /* Init is idempotent (every thread writes the same values), so a race only
|
||||
+ * does redundant work — but the release/acquire pair ensures the table
|
||||
+ * stores are visible before any later encode/decode load on another
|
||||
+ * thread sees ready==1. */
|
||||
+ if (__atomic_load_n(&hpack_huff_tables_ready, __ATOMIC_ACQUIRE))
|
||||
+ return;
|
||||
+
|
||||
+ /* hencs: combine two encode_table entries. {64,0} marks pairs whose
|
||||
+ * combined width overflows 32 bits (handled by the slow loop). */
|
||||
+ for (unsigned i = 0; i < 256; ++i)
|
||||
+ {
|
||||
+ const unsigned bi = encode_table[i].bits;
|
||||
+ const uint32_t ci = encode_table[i].code;
|
||||
+ for (unsigned j = 0; j < 256; ++j)
|
||||
+ {
|
||||
+ const unsigned bj = encode_table[j].bits;
|
||||
+ struct henc *h = &hencs[HENC_I(i, j)];
|
||||
+ if (bi + bj <= 32)
|
||||
+ {
|
||||
+ h->lens = bi + bj;
|
||||
+ h->code = (ci << bj) | encode_table[j].code;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ h->lens = 64;
|
||||
+ h->code = 0;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* hdecs: for each 16-bit prefix, decode up to 3 symbols using a 15-bit
|
||||
+ * direct LUT built from encode_table (codes <= 15 bits cover the common
|
||||
+ * ASCII set; longer codes fall through to the slow path with lens==0). */
|
||||
+ static struct { uint8_t bits, sym; } lut[1 << 15];
|
||||
+ for (unsigned sym = 0; sym < 256; ++sym)
|
||||
+ {
|
||||
+ const unsigned b = encode_table[sym].bits;
|
||||
+ if (b > 15)
|
||||
+ continue;
|
||||
+ const unsigned base = encode_table[sym].code << (15 - b);
|
||||
+ for (unsigned k = 0; k < (1u << (15 - b)); ++k)
|
||||
+ {
|
||||
+ lut[base + k].bits = (uint8_t) b;
|
||||
+ lut[base + k].sym = (uint8_t) sym;
|
||||
+ }
|
||||
+ }
|
||||
+ for (unsigned idx = 0; idx < 65536; ++idx)
|
||||
+ {
|
||||
+ unsigned bits_left = 16, n = 0;
|
||||
+ uint8_t out[3] = {0,0,0};
|
||||
+ while (bits_left >= 5 && n < 3)
|
||||
+ {
|
||||
+ const unsigned rem = idx & ((1u << bits_left) - 1);
|
||||
+ const unsigned key = bits_left >= 15
|
||||
+ ? (rem >> (bits_left - 15))
|
||||
+ : (rem << (15 - bits_left));
|
||||
+ const unsigned b = lut[key].bits;
|
||||
+ if (!b || b > bits_left)
|
||||
+ break;
|
||||
+ out[n++] = lut[key].sym;
|
||||
+ bits_left -= b;
|
||||
+ }
|
||||
+ if (n)
|
||||
+ {
|
||||
+ hdecs[idx].lens = (uint8_t)(((16 - bits_left) << 2) | n);
|
||||
+ hdecs[idx].out[0] = out[0];
|
||||
+ hdecs[idx].out[1] = out[1];
|
||||
+ hdecs[idx].out[2] = out[2];
|
||||
+ }
|
||||
+ /* else: leave zeroed (.bss) — lens==0 triggers slow path */
|
||||
+ }
|
||||
+
|
||||
+ __atomic_store_n(&hpack_huff_tables_ready, 1, __ATOMIC_RELEASE);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
#define HPACK_STATIC_TABLE_SIZE 61
|
||||
#define INITIAL_DYNAMIC_TABLE_SIZE 4096
|
||||
|
||||
@@ -291,6 +384,9 @@
|
||||
int
|
||||
lshpack_enc_init (struct lshpack_enc *enc)
|
||||
{
|
||||
+#if LS_HPACK_USE_LARGE_TABLES && LS_HPACK_BSS_LARGE_TABLES
|
||||
+ lshpack_huff_tables_init();
|
||||
+#endif
|
||||
struct lshpack_double_enc_head *buckets;
|
||||
unsigned nbits = 2;
|
||||
unsigned i;
|
||||
@@ -1270,6 +1366,9 @@
|
||||
void
|
||||
lshpack_dec_init (struct lshpack_dec *dec)
|
||||
{
|
||||
+#if LS_HPACK_USE_LARGE_TABLES && LS_HPACK_BSS_LARGE_TABLES
|
||||
+ lshpack_huff_tables_init();
|
||||
+#endif
|
||||
memset(dec, 0, sizeof(*dec));
|
||||
dec->hpd_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
|
||||
dec->hpd_cur_max_capacity = INITIAL_DYNAMIC_TABLE_SIZE;
|
||||
--- a/huff-tables.h 2026-05-02 06:41:46.300110018 +0000
|
||||
+++ b/huff-tables.h 2026-05-02 06:42:07.509903227 +0000
|
||||
@@ -266,7 +266,7 @@
|
||||
};
|
||||
|
||||
|
||||
-#if LS_HPACK_USE_LARGE_TABLES
|
||||
+#if LS_HPACK_USE_LARGE_TABLES && !LS_HPACK_BSS_LARGE_TABLES
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define I(i,j) ((j<<8)|i)
|
||||
#else
|
||||
@@ -70703,7 +70703,7 @@
|
||||
};
|
||||
|
||||
|
||||
-#if LS_HPACK_USE_LARGE_TABLES
|
||||
+#if LS_HPACK_USE_LARGE_TABLES && !LS_HPACK_BSS_LARGE_TABLES
|
||||
static const struct hdec { uint8_t lens; uint8_t out[3]; } hdecs[] =
|
||||
{
|
||||
/*
|
||||
@@ -0,0 +1,49 @@
|
||||
--- a/lsqpack.c 2026-05-02 06:42:53.039458414 +0000
|
||||
+++ b/lsqpack.c 2026-05-02 06:43:11.469277877 +0000
|
||||
@@ -69,6 +69,15 @@
|
||||
#include <xxhash.h>
|
||||
#endif
|
||||
|
||||
+#if LS_QPACK_USE_LARGE_TABLES && LS_HPACK_BSS_LARGE_TABLES
|
||||
+/* Bun: share lshpack's .bss-backed hencs/hdecs (same RFC 7541 Huffman code)
|
||||
+ * instead of carrying a second 768 KB copy in .rodata. */
|
||||
+struct henc { unsigned lens; uint32_t code; };
|
||||
+struct hdec { uint8_t lens; uint8_t out[3]; };
|
||||
+extern struct henc hencs[65536];
|
||||
+extern struct hdec hdecs[65536];
|
||||
+extern void lshpack_huff_tables_init(void);
|
||||
+#endif
|
||||
#include "huff-tables.h"
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
@@ -502,6 +511,9 @@
|
||||
unsigned max_risked_streams, enum lsqpack_enc_opts enc_opts,
|
||||
unsigned char *tsu_buf, size_t *tsu_buf_sz)
|
||||
{
|
||||
+#if LS_QPACK_USE_LARGE_TABLES && LS_HPACK_BSS_LARGE_TABLES
|
||||
+ lshpack_huff_tables_init();
|
||||
+#endif
|
||||
struct lsqpack_double_enc_head *buckets;
|
||||
unsigned char *p;
|
||||
unsigned nbits = 2;
|
||||
@@ -2782,6 +2794,9 @@
|
||||
unsigned dyn_table_size, unsigned max_risked_streams,
|
||||
const struct lsqpack_dec_hset_if *dh_if, enum lsqpack_dec_opts opts)
|
||||
{
|
||||
+#if LS_QPACK_USE_LARGE_TABLES && LS_HPACK_BSS_LARGE_TABLES
|
||||
+ lshpack_huff_tables_init();
|
||||
+#endif
|
||||
unsigned i;
|
||||
memset(dec, 0, sizeof(*dec));
|
||||
dec->qpd_opts = opts;
|
||||
--- a/huff-tables.h 2026-05-02 06:42:53.039458414 +0000
|
||||
+++ b/huff-tables.h 2026-05-02 06:43:11.459277975 +0000
|
||||
@@ -5141,7 +5141,7 @@
|
||||
},
|
||||
};
|
||||
|
||||
-#if LS_QPACK_USE_LARGE_TABLES
|
||||
+#if LS_QPACK_USE_LARGE_TABLES && !LS_HPACK_BSS_LARGE_TABLES
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define I(i,j) ((j<<8)|i)
|
||||
#else
|
||||
@@ -0,0 +1,17 @@
|
||||
--- a/src/liblsquic/lsquic_full_conn_ietf.c
|
||||
+++ b/src/liblsquic/lsquic_full_conn_ietf.c
|
||||
@@ -9258,6 +9258,14 @@
|
||||
LSQ_INFO("abort error: is_app: %d; error code: %u; error str: %s",
|
||||
is_app, error_code, err_str);
|
||||
ABORT_QUIETLY(is_app, error_code, "%s", err_str);
|
||||
+ /* bun: ABORT_QUIETLY only raises IFC_ERROR; it does not make the
|
||||
+ * connection tickable. immediate_close() -- which turns that error into
|
||||
+ * the CONNECTION_CLOSE frame -- only runs from the connection's tick, so
|
||||
+ * without this the frame waits for whatever ticks the connection next
|
||||
+ * (an idle or retransmission alarm, potentially seconds away) even though
|
||||
+ * the application asked to abort now. ci_close() marks it for the same
|
||||
+ * reason. */
|
||||
+ lsquic_engine_add_conn_to_tickable(conn->ifc_enpub, lconn);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
--- a/src/liblsquic/lsquic_enc_sess_ietf.c
|
||||
+++ b/src/liblsquic/lsquic_enc_sess_ietf.c
|
||||
@@ -1331,14 +1331,7 @@
|
||||
#endif
|
||||
if (!server_name)
|
||||
{
|
||||
- if (enc_sess->esi_enpub->enp_flags & ENPUB_HTTP)
|
||||
- {
|
||||
- LSQ_DEBUG("SNI is not set, but is required in HTTP/3: "
|
||||
- "fail certificate lookup");
|
||||
- return 0;
|
||||
- }
|
||||
- else
|
||||
- LSQ_DEBUG("cert lookup: server name is not set");
|
||||
+ LSQ_DEBUG("cert lookup: server name is not set");
|
||||
}
|
||||
|
||||
path = enc_sess->esi_conn->cn_if->ci_get_path(enc_sess->esi_conn, NULL);
|
||||
@@ -0,0 +1,69 @@
|
||||
--- a/src/liblsquic/lsquic_engine.c 2026-07-07 16:55:39
|
||||
+++ b/src/liblsquic/lsquic_engine.c 2026-07-07 16:55:39
|
||||
@@ -2906,6 +2906,22 @@
|
||||
while (++iov < end);
|
||||
|
||||
return size;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* Give back the packets already staged into the current, not-yet-finalized
|
||||
+ * out_spec. A coalescing bail-out (encrypt failure, OOM, copy failure) only
|
||||
+ * returned the packet it failed on; the ones batched before it had already
|
||||
+ * been removed from the scheduled queue, so nothing sent them and nothing
|
||||
+ * freed them. Reverse order, to match send_batch()'s ordering rule.
|
||||
+ */
|
||||
+static void
|
||||
+return_staged_packets (lsquic_conn_t *conn,
|
||||
+ struct lsquic_packet_out **first,
|
||||
+ struct lsquic_packet_out **past_last)
|
||||
+{
|
||||
+ while (past_last > first)
|
||||
+ conn->cn_if->ci_packet_not_sent(conn, *--past_last);
|
||||
}
|
||||
|
||||
|
||||
@@ -2916,7 +2932,7 @@
|
||||
{
|
||||
unsigned n, w, n_sent, n_batches_sent;
|
||||
lsquic_packet_out_t *packet_out;
|
||||
- struct lsquic_packet_out **packet;
|
||||
+ struct lsquic_packet_out **packet, **packet_spec;
|
||||
lsquic_conn_t *conn;
|
||||
struct out_batch *const batch = &engine->out_batch;
|
||||
struct iovec *iov, *packet_iov;
|
||||
@@ -2950,6 +2966,7 @@
|
||||
continue;
|
||||
}
|
||||
batch->outs[n].iov = packet_iov = iov;
|
||||
+ packet_spec = packet;
|
||||
next_coa:
|
||||
if (!(packet_out->po_flags & (PO_ENCRYPTED|PO_NOENCRYPT)))
|
||||
{
|
||||
@@ -2959,10 +2976,16 @@
|
||||
case ENCPA_NOMEM:
|
||||
/* Send what we have and wait for a more opportune moment */
|
||||
conn->cn_if->ci_packet_not_sent(conn, packet_out);
|
||||
+ return_staged_packets(conn, packet_spec, packet);
|
||||
+ packet = packet_spec;
|
||||
+ iov = packet_iov;
|
||||
goto end_for;
|
||||
case ENCPA_BADCRYPT:
|
||||
/* This is pretty bad: close connection immediately */
|
||||
conn->cn_if->ci_packet_not_sent(conn, packet_out);
|
||||
+ return_staged_packets(conn, packet_spec, packet);
|
||||
+ packet = packet_spec;
|
||||
+ iov = packet_iov;
|
||||
LSQ_INFOC("conn %"CID_FMT" has unsendable packets",
|
||||
CID_BITS(lsquic_conn_log_cid(conn)));
|
||||
if (!(conn->cn_flags & LSCONN_EVANESCENT))
|
||||
@@ -2982,6 +3005,9 @@
|
||||
{
|
||||
/* Copy can only fail if packet could not be allocated */
|
||||
conn->cn_if->ci_packet_not_sent(conn, packet_out);
|
||||
+ return_staged_packets(conn, packet_spec, packet);
|
||||
+ packet = packet_spec;
|
||||
+ iov = packet_iov;
|
||||
goto end_for;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
--- a/src/liblsquic/lsquic_full_conn_ietf.c
|
||||
+++ b/src/liblsquic/lsquic_full_conn_ietf.c
|
||||
@@ -4669,4 +4669,11 @@
|
||||
*/
|
||||
- packet_out = lsquic_send_ctl_new_packet_out(&conn->ifc_send_ctl, 0, PNS_APP,
|
||||
+ enum packnum_space pns; /* bun: highest PNS with keys (RFC 9000 s10.2.3) */
|
||||
+ if (conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|
||||
+ pns = PNS_APP;
|
||||
+ else if (conn->ifc_flags & IFC_IGNORE_INIT)
|
||||
+ pns = PNS_HSK;
|
||||
+ else
|
||||
+ pns = PNS_INIT;
|
||||
+ packet_out = lsquic_send_ctl_new_packet_out(&conn->ifc_send_ctl, 0, pns,
|
||||
CUR_NPATH(conn));
|
||||
if (!packet_out)
|
||||
--- a/src/liblsquic/lsquic_mini_conn_ietf.h
|
||||
+++ b/src/liblsquic/lsquic_mini_conn_ietf.h
|
||||
@@ -91,6 +91,10 @@
|
||||
lsquic_time_t imc_largest_recvd[IMICO_N_PNS];
|
||||
struct lsquic_rtt_stats imc_rtt_stats;
|
||||
enum trans_error_code imc_error_code;
|
||||
+ /* bun: the peer's CONNECTION_CLOSE code (62-bit; the enum above cannot
|
||||
+ * hold it), valid when IMC_CLOSE_RECVD is set and IMC_PARSE_FAILED is
|
||||
+ * not. */
|
||||
+ uint64_t imc_close_code;
|
||||
unsigned imc_bytes_in;
|
||||
unsigned imc_bytes_out;
|
||||
unsigned short imc_crypto_frames_sz;
|
||||
--- a/src/liblsquic/lsquic_mini_conn_ietf.c
|
||||
+++ b/src/liblsquic/lsquic_mini_conn_ietf.c
|
||||
@@ -1346,6 +1346,9 @@
|
||||
}
|
||||
EV_LOG_CONNECTION_CLOSE_FRAME_IN(LSQUIC_LOG_CONN_ID, error_code,
|
||||
(int) reason_len, (const char *) p + reason_off);
|
||||
+ /* bun: keep the peer's code so on_mini_conn_failed can report a peer
|
||||
+ * close instead of inventing a handshake failure. */
|
||||
+ conn->imc_close_code = error_code;
|
||||
LSQ_INFO("Received CONNECTION_CLOSE frame (%s-level code: %"PRIu64"; "
|
||||
"reason: %.*s)", app_error ? "application" : "transport",
|
||||
error_code, (int) reason_len, (const char *) p + reason_off);
|
||||
--- a/src/liblsquic/lsquic_engine.c
|
||||
+++ b/src/liblsquic/lsquic_engine.c
|
||||
@@ -982,6 +982,12 @@
|
||||
error_code = 0x100 + imc->imc_tls_alert;
|
||||
else if (imc->imc_flags & IMC_BAD_TRANS_PARAMS)
|
||||
error_code = TEC_TRANSPORT_PARAMETER_ERROR;
|
||||
+ else if ((imc->imc_flags & (IMC_CLOSE_RECVD|IMC_PARSE_FAILED))
|
||||
+ == IMC_CLOSE_RECVD)
|
||||
+ /* bun: the peer closed during the handshake. QUIC codes fit in
|
||||
+ * 62 bits, so bit 63 marks "peer's own CONNECTION_CLOSE" and the
|
||||
+ * low bits carry its code. */
|
||||
+ error_code = (1ULL << 63) | imc->imc_close_code;
|
||||
else
|
||||
error_code = TEC_NO_ERROR;
|
||||
if (0 == lsquic_conn_get_sockaddr(conn, &local_sa, &peer_sa))
|
||||
--- a/src/liblsquic/lsquic_enc_sess_ietf.c
|
||||
+++ b/src/liblsquic/lsquic_enc_sess_ietf.c
|
||||
@@ -2368,8 +2368,21 @@
|
||||
}
|
||||
|
||||
enc_level = hety2el[packet_in->pi_header_type];
|
||||
+ /* bun: an Initial in the version used before a compatible version switch
|
||||
+ * (RFC 9368) -- e.g. the peer's CONNECTION_CLOSE sent before it processed
|
||||
+ * our packets -- needs the keys iquic_esfi_switch_version saved; the
|
||||
+ * current slot holds the new version's. The wire version field picks the
|
||||
+ * key set deterministically. */
|
||||
+ struct hsk_crypto *vn_hsk;
|
||||
+ if (enc_level == ENC_LEV_INIT && enc_sess->esi_vn_save
|
||||
+ && packet_in->pi_version != enc_sess->esi_conn->cn_version)
|
||||
+ vn_hsk = enc_sess->esi_vn_save;
|
||||
+ else
|
||||
+ vn_hsk = NULL;
|
||||
if (enc_level == ENC_LEV_APP)
|
||||
hp = &enc_sess->esi_hp;
|
||||
+ else if (vn_hsk)
|
||||
+ hp = &vn_hsk->hp;
|
||||
else if (enc_sess->esi_hsk_crypto)
|
||||
hp = &enc_sess->esi_hsk_crypto[ enc_level ].hp;
|
||||
else
|
||||
@@ -2455,8 +2468,11 @@
|
||||
else
|
||||
{
|
||||
key_phase = 0;
|
||||
- assert(enc_sess->esi_hsk_crypto);
|
||||
- pair = &enc_sess->esi_hsk_crypto[ enc_level ].pair;
|
||||
+ assert(enc_sess->esi_hsk_crypto || vn_hsk);
|
||||
+ if (vn_hsk)
|
||||
+ pair = &vn_hsk->pair;
|
||||
+ else
|
||||
+ pair = &enc_sess->esi_hsk_crypto[ enc_level ].pair;
|
||||
crypto_ctx = &pair->ykp_ctx[ 0 ];
|
||||
if (UNLIKELY(0 == (crypto_ctx->yk_flags & YK_INITED)))
|
||||
{
|
||||
@@ -0,0 +1,111 @@
|
||||
--- a/src/liblsquic/lsquic_gquic_stubs.c
|
||||
+++ b/src/liblsquic/lsquic_gquic_stubs.c
|
||||
@@ -0,0 +1,108 @@
|
||||
+/* Bun: gQUIC (Google QUIC) is unreachable — Bun.serve and the H3 client only
|
||||
+ * negotiate IETF QUIC versions (see packages/bun-usockets/src/quic.c, which
|
||||
+ * masks es_versions with LSQUIC_IETF_VERSIONS). The real gQUIC sources pull
|
||||
+ * in ~130 KB of common_cert_set tables plus the legacy handshake/parser. We
|
||||
+ * compile this stub instead so the few unconditional references from
|
||||
+ * lsquic_engine.c / lsquic_global.c / lsquic_pr_queue.c / lsquic_stream.c
|
||||
+ * still link, while LTO drops everything behind them. None of these are
|
||||
+ * reached at runtime when no gQUIC version bit is set. */
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <stddef.h>
|
||||
+#include <sys/queue.h>
|
||||
+
|
||||
+#include "lsquic.h"
|
||||
+#include "lsquic_types.h"
|
||||
+#include "lsquic_int_types.h"
|
||||
+#include "lsquic_packet_common.h"
|
||||
+#include "lsquic_packet_gquic.h"
|
||||
+#include "lsquic_packet_in.h"
|
||||
+#include "lsquic_parse_common.h"
|
||||
+#include "lsquic_parse.h"
|
||||
+#include "lsquic_enc_sess.h"
|
||||
+#include "lsquic_headers_stream.h"
|
||||
+
|
||||
+struct lsquic_engine_public;
|
||||
+
|
||||
+static int gquic_stub_global_init (int flags) { (void) flags; return 0; }
|
||||
+static void gquic_stub_global_cleanup (void) { }
|
||||
+
|
||||
+#ifdef NDEBUG
|
||||
+const
|
||||
+#endif
|
||||
+struct enc_session_funcs_common lsquic_enc_session_common_gquic_1 = {
|
||||
+ .esf_global_init = gquic_stub_global_init,
|
||||
+ .esf_global_cleanup = gquic_stub_global_cleanup,
|
||||
+};
|
||||
+
|
||||
+#ifdef NDEBUG
|
||||
+const
|
||||
+#endif
|
||||
+struct enc_session_funcs_common lsquic_enc_session_common_gquic_2 = {
|
||||
+ .esf_global_init = gquic_stub_global_init,
|
||||
+ .esf_global_cleanup = gquic_stub_global_cleanup,
|
||||
+};
|
||||
+
|
||||
+#ifdef NDEBUG
|
||||
+const
|
||||
+#endif
|
||||
+struct enc_session_funcs_gquic lsquic_enc_session_gquic_gquic_1;
|
||||
+
|
||||
+/* lsquic_pr_queue.c pre-builds a gQUIC reset packet at server init via
|
||||
+ * select_pf_by_ver(LSQVER_043)->pf_generate_simple_prst. Return a one-byte
|
||||
+ * placeholder so the assert(len > 0)/len < 0 checks pass; the buffer is only
|
||||
+ * sent for PACKET_REQ_PUBRES which is never queued without a gQUIC version. */
|
||||
+static ssize_t gquic_stub_generate_simple_prst (const lsquic_cid_t *cid,
|
||||
+ unsigned char *buf, size_t sz)
|
||||
+{
|
||||
+ (void) cid;
|
||||
+ if (sz) buf[0] = 0;
|
||||
+ return sz ? 1 : 0;
|
||||
+}
|
||||
+
|
||||
+const struct parse_funcs lsquic_parse_funcs_gquic_Q043 = {
|
||||
+ .pf_generate_simple_prst = gquic_stub_generate_simple_prst,
|
||||
+};
|
||||
+const struct parse_funcs lsquic_parse_funcs_gquic_Q046;
|
||||
+const struct parse_funcs lsquic_parse_funcs_gquic_Q050;
|
||||
+
|
||||
+int lsquic_Q050_parse_packet_in_long_begin (struct lsquic_packet_in *p,
|
||||
+ size_t l, int s, unsigned c, struct packin_parse_state *st)
|
||||
+{ (void)p;(void)l;(void)s;(void)c;(void)st; return -1; }
|
||||
+
|
||||
+struct lsquic_conn *
|
||||
+lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *e,
|
||||
+ unsigned v, unsigned f, const char *h, unsigned short m, int i,
|
||||
+ const unsigned char *r, size_t rl)
|
||||
+{ (void)e;(void)v;(void)f;(void)h;(void)m;(void)i;(void)r;(void)rl;
|
||||
+ errno = EPROTONOSUPPORT; return NULL; }
|
||||
+
|
||||
+struct lsquic_conn *
|
||||
+lsquic_gquic_full_conn_server_new (struct lsquic_engine_public *e,
|
||||
+ unsigned f, struct lsquic_conn *m)
|
||||
+{ (void)e;(void)f;(void)m; errno = EPROTONOSUPPORT; return NULL; }
|
||||
+
|
||||
+lsquic_conn_t *
|
||||
+lsquic_mini_conn_new (struct lsquic_engine_public *e,
|
||||
+ const struct lsquic_packet_in *p, enum lsquic_version v)
|
||||
+{ (void)e;(void)p;(void)v; return NULL; }
|
||||
+
|
||||
+int lsquic_headers_stream_send_headers (struct headers_stream *hs,
|
||||
+ lsquic_stream_id_t id, const struct lsquic_http_headers *h, int eos,
|
||||
+ unsigned w)
|
||||
+{ (void)hs;(void)id;(void)h;(void)eos;(void)w; return -1; }
|
||||
+
|
||||
+int lsquic_headers_stream_send_priority (struct headers_stream *hs,
|
||||
+ lsquic_stream_id_t id, int ex, lsquic_stream_id_t dep, unsigned w)
|
||||
+{ (void)hs;(void)id;(void)ex;(void)dep;(void)w; return -1; }
|
||||
+
|
||||
+struct lsquic_stream *
|
||||
+lsquic_headers_stream_get_stream (const struct headers_stream *hs)
|
||||
+{ (void)hs; return NULL; }
|
||||
+
|
||||
+/* lsquic_engine.c calls these unconditionally; the real impls allocate gQUIC
|
||||
+ * X25519/TLS state. With no gQUIC versions enabled they're never used. */
|
||||
+int lsquic_init_gquic_crypto (struct lsquic_engine_public *enpub)
|
||||
+{ (void)enpub; return 0; }
|
||||
+void lsquic_cleanup_gquic_crypto (struct lsquic_engine_public *enpub)
|
||||
+{ (void)enpub; }
|
||||
@@ -0,0 +1,58 @@
|
||||
--- a/src/liblsquic/lsquic_hash.h
|
||||
+++ b/src/liblsquic/lsquic_hash.h
|
||||
@@ -69,6 +69,19 @@
|
||||
struct lsquic_hash_elem *
|
||||
lsquic_hash_next (struct lsquic_hash *);
|
||||
|
||||
+/* lsquic_hash_first()/_next() keep their cursor INSIDE the hash, so a second
|
||||
+ * walk started while one is in progress resets the first one's position. Use
|
||||
+ * these when a nested walk of the same hash is possible: the caller holds the
|
||||
+ * cursor, so nesting is safe. */
|
||||
+struct lsquic_hash_elem *
|
||||
+lsquic_hash_first_nested (struct lsquic_hash *);
|
||||
+
|
||||
+static inline struct lsquic_hash_elem *
|
||||
+lsquic_hash_next_nested (struct lsquic_hash_elem *el)
|
||||
+{
|
||||
+ return TAILQ_NEXT(el, qhe_next_all);
|
||||
+}
|
||||
+
|
||||
unsigned
|
||||
lsquic_hash_count (struct lsquic_hash *);
|
||||
|
||||
--- a/src/liblsquic/lsquic_hash.c
|
||||
+++ b/src/liblsquic/lsquic_hash.c
|
||||
@@ -209,6 +209,13 @@
|
||||
}
|
||||
|
||||
|
||||
+struct lsquic_hash_elem *
|
||||
+lsquic_hash_first_nested (struct lsquic_hash *hash)
|
||||
+{
|
||||
+ return TAILQ_FIRST(&hash->qh_all);
|
||||
+}
|
||||
+
|
||||
+
|
||||
void
|
||||
lsquic_hash_reset_iter (struct lsquic_hash *hash)
|
||||
{
|
||||
--- a/src/liblsquic/lsquic_send_ctl.c
|
||||
+++ b/src/liblsquic/lsquic_send_ctl.c
|
||||
@@ -2984,9 +2984,15 @@
|
||||
if (!(ctl->sc_conn_pub->cp_flags & CP_HAVE_PRIO))
|
||||
return BPT_HIGHEST_PRIO;
|
||||
|
||||
+ /* Nest-safe walk: ietf_full_conn_ci_close() iterates all_streams with the
|
||||
+ * hash's shared cursor and resets each bidi stream; when the send
|
||||
+ * controller cannot send immediately that RESET_STREAM takes the buffered
|
||||
+ * path and lands here. lsquic_hash_first() would reset the cursor the
|
||||
+ * close loop is walking, restarting it over a hash whose elements it is
|
||||
+ * destroying. */
|
||||
all_streams = ctl->sc_conn_pub->all_streams;
|
||||
- for (el = lsquic_hash_first(all_streams); el;
|
||||
- el = lsquic_hash_next(all_streams))
|
||||
+ for (el = lsquic_hash_first_nested(all_streams); el;
|
||||
+ el = lsquic_hash_next_nested(el))
|
||||
{
|
||||
other_stream = lsquic_hashelem_getdata(el);
|
||||
if (other_stream != stream
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
send_batch: requeue every packet in an unsent coalesced datagram
|
||||
|
||||
`off` is `unsigned`, so when the first unsent spec in a batch is at
|
||||
index 0 and coalesces multiple packets, `&batch->packets[off - 1]`
|
||||
indexes with UINT_MAX and `end` lands far past the array. The
|
||||
`--packet_out > end` condition is then false after the first iteration
|
||||
and only the last packet of the coalesced group is returned to the
|
||||
connection; the earlier ones (typically the INIT ACK and the HSK
|
||||
CRYPTO carrying the client Finished) are silently dropped and never
|
||||
retransmitted, so the peer never completes the handshake.
|
||||
|
||||
Rewriting the bounds as [off, off+count) avoids the underflow while
|
||||
preserving the reverse iteration order that send_ctl_sched_prepend
|
||||
relies on.
|
||||
|
||||
--- a/src/liblsquic/lsquic_engine.c
|
||||
+++ b/src/liblsquic/lsquic_engine.c
|
||||
@@ -2739,12 +2739,12 @@
|
||||
off = batch->pack_off[i];
|
||||
count = batch->outs[i].iovlen;
|
||||
assert(count > 0);
|
||||
- packet_out = &batch->packets[off + count - 1];
|
||||
- end = &batch->packets[off - 1];
|
||||
+ packet_out = &batch->packets[off + count];
|
||||
+ end = &batch->packets[off];
|
||||
do
|
||||
batch->conns[i]->cn_if->ci_packet_not_sent(batch->conns[i],
|
||||
- *packet_out);
|
||||
- while (--packet_out > end);
|
||||
+ *--packet_out);
|
||||
+ while (packet_out > end);
|
||||
if (!(batch->conns[i]->cn_flags & (LSCONN_COI_ACTIVE|LSCONN_EVANESCENT)))
|
||||
coi_reactivate(sb_ctx->conns_iter, batch->conns[i]);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
--- a/src/liblsquic/lsquic_conn_public.h 2026-04-20 23:16:15.000000000 +0000
|
||||
+++ b/src/liblsquic/lsquic_conn_public.h 2026-04-27 08:51:00.711139257 +0000
|
||||
@@ -49,6 +49,7 @@
|
||||
} u;
|
||||
enum {
|
||||
CP_STREAM_UNBLOCKED = 1 << 0, /* Set when a stream becomes unblocked */
|
||||
+ CP_HAVE_PRIO = 1 << 1, /* Set once any stream gets a non-default priority */
|
||||
} cp_flags;
|
||||
struct lsquic_send_ctl *send_ctl;
|
||||
#if LSQUIC_CONN_STATS
|
||||
--- a/src/liblsquic/lsquic_send_ctl.c 2026-04-20 23:16:15.000000000 +0000
|
||||
+++ b/src/liblsquic/lsquic_send_ctl.c 2026-04-27 08:51:00.711139257 +0000
|
||||
@@ -2963,6 +2963,11 @@
|
||||
struct lsquic_hash_elem *el;
|
||||
struct lsquic_hash *all_streams;
|
||||
|
||||
+ /* When no stream on this connection has ever been assigned a
|
||||
+ * non-default priority, the loop below cannot find a lower one. */
|
||||
+ if (!(ctl->sc_conn_pub->cp_flags & CP_HAVE_PRIO))
|
||||
+ return BPT_HIGHEST_PRIO;
|
||||
+
|
||||
all_streams = ctl->sc_conn_pub->all_streams;
|
||||
for (el = lsquic_hash_first(all_streams); el;
|
||||
el = lsquic_hash_next(all_streams))
|
||||
--- a/src/liblsquic/lsquic_stream.c 2026-04-20 23:16:15.000000000 +0000
|
||||
+++ b/src/liblsquic/lsquic_stream.c 2026-04-27 08:51:00.711139257 +0000
|
||||
@@ -4677,6 +4677,7 @@
|
||||
{
|
||||
if (0 == lsquic_stream_set_priority_internal(stream, priority))
|
||||
{
|
||||
+ stream->conn_pub->cp_flags |= CP_HAVE_PRIO;
|
||||
if (stream->sm_bflags & SMBF_IETF)
|
||||
{
|
||||
if (stream->sm_bflags & SMBF_HTTP_PRIO)
|
||||
@@ -5589,6 +5590,7 @@
|
||||
return -1;
|
||||
}
|
||||
stream->sm_priority = ehp->urgency;
|
||||
+ stream->conn_pub->cp_flags |= CP_HAVE_PRIO;
|
||||
if (ehp->incremental)
|
||||
stream->sm_bflags |= SMBF_INCREMENTAL;
|
||||
else
|
||||
@@ -0,0 +1,204 @@
|
||||
diff -urN a/src/liblsquic/lsquic_versions_to_string.c b/src/liblsquic/lsquic_versions_to_string.c
|
||||
--- a/src/liblsquic/lsquic_versions_to_string.c 1970-01-01 00:00:00.000000000 +0000
|
||||
+++ b/src/liblsquic/lsquic_versions_to_string.c 2026-04-27 01:30:38.609686072 +0000
|
||||
@@ -0,0 +1,200 @@
|
||||
+/*
|
||||
+ * Auto-generated by gen-verstrs.pl - committed for DirectBuild
|
||||
+ */
|
||||
+
|
||||
+#include <assert.h>
|
||||
+#include <string.h>
|
||||
+
|
||||
+#include "lsquic.h"
|
||||
+
|
||||
+struct lsquic_engine;
|
||||
+
|
||||
+static const char *const versions_to_string[ 1 << N_LSQVER ] = {
|
||||
+ [0] = "",
|
||||
+ [(1<<LSQVER_043)] = "43",
|
||||
+ [(1<<LSQVER_046)] = "46",
|
||||
+ [(1<<LSQVER_043)|(1<<LSQVER_046)] = "43,46",
|
||||
+};
|
||||
+
|
||||
+
|
||||
+const char *
|
||||
+lsquic_get_alt_svc_versions (unsigned versions)
|
||||
+{
|
||||
+ /* Limit to versions in versions_to_string: */
|
||||
+ versions &= ((1<<LSQVER_043)|(1<<LSQVER_046));
|
||||
+ return versions_to_string[ versions ];
|
||||
+}
|
||||
+
|
||||
+static const struct {
|
||||
+ unsigned versions;
|
||||
+ const char *h3_alpns[8];
|
||||
+} vers_2_h3_alnps[] = {
|
||||
+ { 0, { NULL }},
|
||||
+ {(1<<LSQVER_043), { "h3-Q043", NULL }},
|
||||
+ {(1<<LSQVER_046), { "h3-Q046", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046), { "h3-Q043", "h3-Q046", NULL }},
|
||||
+ {(1<<LSQVER_050), { "h3-Q050", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050), { "h3-Q043", "h3-Q050", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050), { "h3-Q046", "h3-Q050", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050), { "h3-Q043", "h3-Q046", "h3-Q050", NULL }},
|
||||
+ {(1<<LSQVER_ID27), { "h3-27", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID27), { "h3-Q043", "h3-27", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID27), { "h3-Q046", "h3-27", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID27), { "h3-Q043", "h3-Q046", "h3-27", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID27), { "h3-Q050", "h3-27", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID27), { "h3-Q043", "h3-Q050", "h3-27", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27), { "h3-Q046", "h3-Q050", "h3-27", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-27", NULL }},
|
||||
+ {(1<<LSQVER_ID29), { "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID29), { "h3-Q043", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID29), { "h3-Q046", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID29), { "h3-Q043", "h3-Q046", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID29), { "h3-Q050", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID29), { "h3-Q043", "h3-Q050", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID29), { "h3-Q046", "h3-Q050", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID29), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_ID27)|(1<<LSQVER_ID29), { "h3-27", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29), { "h3-Q043", "h3-27", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29), { "h3-Q046", "h3-27", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29), { "h3-Q043", "h3-Q046", "h3-27", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29), { "h3-Q050", "h3-27", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29), { "h3-Q043", "h3-Q050", "h3-27", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29), { "h3-Q046", "h3-Q050", "h3-27", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-27", "h3-29", NULL }},
|
||||
+ {(1<<LSQVER_I001), { "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_I001), { "h3-Q043", "h3", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_I001), { "h3-Q046", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q046", "h3", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_I001), { "h3-Q050", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q050", "h3", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_I001), { "h3-Q046", "h3-Q050", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q046", "h3-Q050", "h3", NULL }},
|
||||
+ {(1<<LSQVER_ID27)|(1<<LSQVER_I001), { "h3-27", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID27)|(1<<LSQVER_I001), { "h3-Q043", "h3-27", "h3", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_I001), { "h3-Q046", "h3-27", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q046", "h3-27", "h3", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I001), { "h3-Q050", "h3-27", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q050", "h3-27", "h3", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I001), { "h3-Q046", "h3-Q050", "h3-27", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-27", "h3", NULL }},
|
||||
+ {(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q043", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q046", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q046", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q050", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q050", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q046", "h3-Q050", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-27", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q043", "h3-27", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q046", "h3-27", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q046", "h3-27", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q050", "h3-27", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q050", "h3-27", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q046", "h3-Q050", "h3-27", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-27", "h3-29", "h3", NULL }},
|
||||
+ {(1<<LSQVER_I002), { "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_I002), { "h3-Q043", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_I002), { "h3-Q046", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_I002), { "h3-Q050", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q050", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_I002), { "h3-Q046", "h3-Q050", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-Q050", "", NULL }},
|
||||
+ {(1<<LSQVER_ID27)|(1<<LSQVER_I002), { "h3-27", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID27)|(1<<LSQVER_I002), { "h3-Q043", "h3-27", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_I002), { "h3-Q046", "h3-27", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-27", "", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I002), { "h3-Q050", "h3-27", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q050", "h3-27", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I002), { "h3-Q046", "h3-Q050", "h3-27", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-27", "", NULL }},
|
||||
+ {(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q043", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q046", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q050", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q050", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q046", "h3-Q050", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-27", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q043", "h3-27", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q046", "h3-27", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-27", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q050", "h3-27", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q050", "h3-27", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q046", "h3-Q050", "h3-27", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-27", "h3-29", "", NULL }},
|
||||
+ {(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q046", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q050", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q050", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q046", "h3-Q050", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-Q050", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_ID27)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-27", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID27)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-27", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q046", "h3-27", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-27", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q050", "h3-27", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q050", "h3-27", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q046", "h3-Q050", "h3-27", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-27", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q046", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q050", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q050", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q046", "h3-Q050", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-27", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-27", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q046", "h3-27", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-27", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q050", "h3-27", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q050", "h3-27", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q046", "h3-Q050", "h3-27", "h3-29", "h3", "", NULL }},
|
||||
+ {(1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002), { "h3-Q043", "h3-Q046", "h3-Q050", "h3-27", "h3-29", "h3", "", NULL }},
|
||||
+};
|
||||
+
|
||||
+const char *const *
|
||||
+lsquic_get_h3_alpns (unsigned versions)
|
||||
+{
|
||||
+ unsigned i;
|
||||
+
|
||||
+ versions &= ((1<<LSQVER_043)|(1<<LSQVER_046)|(1<<LSQVER_050)|(1<<LSQVER_ID27)|(1<<LSQVER_ID29)|(1<<LSQVER_I001)|(1<<LSQVER_I002));
|
||||
+
|
||||
+ for (i = 0; i < sizeof(vers_2_h3_alnps) / sizeof(vers_2_h3_alnps[0]); ++i)
|
||||
+ if (versions == vers_2_h3_alnps[i].versions)
|
||||
+ return vers_2_h3_alnps[i].h3_alpns;
|
||||
+
|
||||
+ assert(0);
|
||||
+ return vers_2_h3_alnps[0].h3_alpns;
|
||||
+}
|
||||
+
|
||||
+enum lsquic_version
|
||||
+lsquic_alpn2ver (const char *alpn, size_t len)
|
||||
+{
|
||||
+ static const struct el {
|
||||
+ size_t len;
|
||||
+ char alpn[10];
|
||||
+ enum lsquic_version version;
|
||||
+ } map[] = {
|
||||
+ {sizeof("h3-Q043")-1,"h3-Q043", LSQVER_043},
|
||||
+ {sizeof("h3-Q046")-1,"h3-Q046", LSQVER_046},
|
||||
+ {sizeof("h3-Q050")-1,"h3-Q050", LSQVER_050},
|
||||
+ {sizeof("h3-27")-1,"h3-27", LSQVER_ID27},
|
||||
+ {sizeof("h3-29")-1,"h3-29", LSQVER_ID29},
|
||||
+ {sizeof("h3")-1,"h3", LSQVER_I001},
|
||||
+ };
|
||||
+ const struct el *el;
|
||||
+
|
||||
+ if (alpn)
|
||||
+ for (el = map; el < map + sizeof(map) / sizeof(map[0]); ++el)
|
||||
+ if (el->len == len && 0 == strncmp(el->alpn, alpn, len))
|
||||
+ return el->version;
|
||||
+
|
||||
+ return -1;
|
||||
+}
|
||||
@@ -0,0 +1,12 @@
|
||||
--- tcc.h
|
||||
+++ tcc.h
|
||||
@@ -23,7 +23,9 @@
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#define _DARWIN_C_SOURCE
|
||||
+#if __has_include("config.h")
|
||||
#include "config.h"
|
||||
+#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
@@ -0,0 +1,55 @@
|
||||
clang-cl on Windows ARM64 defines _MSC_VER but is not real MSVC: it ships
|
||||
clang's own <arm_neon.h>/<arm_acle.h>. MSVC's <arm64_neon.h> defines
|
||||
vld1q_u16_x4/vld1q_u8_x4/vst1q_u16_x4 as macros, which macro-expand the
|
||||
polyfill function definitions at neon_intrins.h:40-63 into garbage. MSVC's
|
||||
<intrin.h> lacks the ARM ACLE CRC intrinsics (__crc32b etc.) that clang's
|
||||
<arm_acle.h> provides.
|
||||
|
||||
Gate the MSVC-specific includes on !defined(__clang__) so clang-cl takes the
|
||||
standard ACLE/NEON path. Also fix the matching cmake feature-detection probes
|
||||
so HAVE_ARMV8_INTRIN and NEON_HAS_LD4 succeed under clang-cl, which sets
|
||||
ARM_CRC32_INTRIN/ARM_NEON_HASLD4 and skips the polyfills entirely.
|
||||
|
||||
Upstream: not yet reported (zlib-ng CI does not cover clang-cl ARM64).
|
||||
--- a/arch/arm/neon_intrins.h
|
||||
+++ b/arch/arm/neon_intrins.h
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef ARM_NEON_INTRINS_H
|
||||
#define ARM_NEON_INTRINS_H
|
||||
|
||||
-#if defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC))
|
||||
+#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC))
|
||||
/* arm64_neon.h is MSVC specific */
|
||||
# include <arm64_neon.h>
|
||||
#else
|
||||
--- a/arch/arm/acle_intrins.h
|
||||
+++ b/arch/arm/acle_intrins.h
|
||||
@@ -2,7 +2,7 @@
|
||||
#define ARM_ACLE_INTRINS_H
|
||||
|
||||
#include <stdint.h>
|
||||
-#ifdef _MSC_VER
|
||||
+#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# include <intrin.h>
|
||||
#elif defined(HAVE_ARM_ACLE_H)
|
||||
# include <arm_acle.h>
|
||||
--- a/cmake/detect-intrinsics.cmake
|
||||
+++ b/cmake/detect-intrinsics.cmake
|
||||
@@ -42,7 +42,7 @@ macro(check_armv8_compiler_flag)
|
||||
)
|
||||
# Check whether compiler supports ARMv8 intrinsics
|
||||
check_c_source_compiles(
|
||||
- "#if defined(_MSC_VER)
|
||||
+ "#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <arm_acle.h>
|
||||
@@ -285,7 +285,7 @@ macro(check_neon_ld4_intrinsics)
|
||||
# Check whether compiler supports loading 4 neon vecs into a register range
|
||||
set(CMAKE_REQUIRED_FLAGS "${NEONFLAG} ${NATIVEFLAG} ${ZNOLTOFLAG}")
|
||||
check_c_source_compiles(
|
||||
- "#if defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC))
|
||||
+ "#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC))
|
||||
# include <arm64_neon.h>
|
||||
#else
|
||||
# include <arm_neon.h>
|
||||
Reference in New Issue
Block a user