Commit 95144f5b55 for openssl.org

commit 95144f5b5536859ae9cce169deed019df381dcdb
Author: Neil Horman <nhorman@openssl.org>
Date:   Tue Nov 10 09:50:17 2026 -0500

    inline the WPACKET interface

    WPACKET does lots of small writes (8/16/24/32/48/64 bytes), and because
    its not inlined, its really hard for the compiler to optimize.  Further,
    those writes tend to be bytewise, as we do endian shifting in the
    process so its fairly ugly.

    Since WPACKET is used to marshall data in tx/rx hot paths, this has a
    significant performance impace.

    I'm proposing, given that its an internal only api, that we inline it
    all.  Code growth from doing so is not great, but not terrible either
    (30k in libcrypto and 200k in libssl), but it really helps with
    performance, particularly on DTLS which uses WPACKET to marshall lots of
    fixed size headers.

    This change is sensitive to OPENSSL_SMALL_FOOTPRINT and avoids doing
    most of the inlining if that macro is defined, to prevent code growth

    Reviewed-by: Bob Beck <beck@openssl.org>
    Reviewed-by: Andrew Dinh <andrewd@openssl.org>
    Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
    Merge-date: Fri Nov 13 16:58:18 2026
    Merged-from: https://github.com/openssl/openssl/pull/33581

diff --git a/crypto/ml_dsa/ml_dsa_encoders.c b/crypto/ml_dsa/ml_dsa_encoders.c
index 7d04d867c2..0cfbb4f316 100644
--- a/crypto/ml_dsa/ml_dsa_encoders.c
+++ b/crypto/ml_dsa/ml_dsa_encoders.c
@@ -394,6 +394,8 @@ static int poly_encode_signed_two_to_power_12(const POLY *p, WPACKET *pkt)

         if (!WPACKET_allocate_bytes(pkt, 13, &out))
             return 0;
+        if (out == NULL)
+            return 0;

         a1 = mod_sub_64(range, *in++);
         a1 |= mod_sub_64(range, *in++) << 13;
@@ -564,6 +566,8 @@ static int poly_encode_signed_two_to_power_17(const POLY *p, WPACKET *pkt)

         if (!WPACKET_allocate_bytes(pkt, 9, &out))
             return 0;
+        if (out == NULL)
+            return 0;

         z0 = mod_sub(range, *in++);
         z0 |= (z1 = mod_sub(range, *in++)) << 18;
diff --git a/crypto/packet.c b/crypto/packet.c
index 2002f5640b..d57ba0f325 100644
--- a/crypto/packet.c
+++ b/crypto/packet.c
@@ -10,593 +10,155 @@
 #include "internal/cryptlib.h"
 #include <stdint.h>
 #include "internal/packet.h"
+#include "internal/packet_inline.h"
 #if !defined OPENSSL_NO_QUIC && !defined FIPS_MODULE
 #include "internal/packet_quic.h"
 #endif
 #include <openssl/err.h>

-#define DEFAULT_BUF_SIZE 256
-
+#ifdef OPENSSL_SMALL_FOOTPRINT
 int WPACKET_allocate_bytes(WPACKET *pkt, size_t len, unsigned char **allocbytes)
 {
-    if (!WPACKET_reserve_bytes(pkt, len, allocbytes))
-        return 0;
-
-    pkt->written += len;
-    pkt->curr += len;
-    return 1;
+    return WPACKET_allocate_bytes_int(pkt, len, allocbytes);
 }

 int WPACKET_sub_allocate_bytes__(WPACKET *pkt, size_t len,
     unsigned char **allocbytes, size_t lenbytes)
 {
-    if (!WPACKET_start_sub_packet_len__(pkt, lenbytes)
-        || !WPACKET_allocate_bytes(pkt, len, allocbytes)
-        || !WPACKET_close(pkt))
-        return 0;
-
-    return 1;
+    return WPACKET_sub_allocate_bytes___int(pkt, len, allocbytes, lenbytes);
 }

-#define GETBUF(p) (((p)->staticbuf != NULL)         \
-        ? (p)->staticbuf                            \
-        : ((p)->buf != NULL                         \
-                  ? (unsigned char *)(p)->buf->data \
-                  : NULL))
-
 int WPACKET_reserve_bytes(WPACKET *pkt, size_t len, unsigned char **allocbytes)
 {
-    /* Internal API, so should not fail */
-    if (!ossl_assert(pkt->subs != NULL && len != 0))
-        return 0;
-
-    if (pkt->maxsize - pkt->written < len)
-        return 0;
-
-    if (pkt->buf != NULL && (pkt->buf->length - pkt->written < len)) {
-        size_t newlen;
-        size_t reflen;
-
-        reflen = (len > pkt->buf->length) ? len : pkt->buf->length;
-
-        if (reflen > SIZE_MAX / 2) {
-            newlen = SIZE_MAX;
-        } else {
-            newlen = reflen * 2;
-            if (newlen < DEFAULT_BUF_SIZE)
-                newlen = DEFAULT_BUF_SIZE;
-        }
-        if (BUF_MEM_grow(pkt->buf, newlen) == 0)
-            return 0;
-    }
-    if (allocbytes != NULL) {
-        *allocbytes = WPACKET_get_curr(pkt);
-        if (pkt->endfirst && *allocbytes != NULL)
-            *allocbytes -= len;
-    }
-
-    return 1;
+    return WPACKET_reserve_bytes_int(pkt, len, allocbytes);
 }

 int WPACKET_sub_reserve_bytes__(WPACKET *pkt, size_t len,
     unsigned char **allocbytes, size_t lenbytes)
 {
-    if (pkt->endfirst && lenbytes > 0)
-        return 0;
-
-    if (!WPACKET_reserve_bytes(pkt, lenbytes + len, allocbytes))
-        return 0;
-
-    if (*allocbytes != NULL)
-        *allocbytes += lenbytes;
-
-    return 1;
-}
-
-static size_t maxmaxsize(size_t lenbytes)
-{
-    if (lenbytes >= sizeof(size_t) || lenbytes == 0)
-        return SIZE_MAX;
-
-    return ((size_t)1 << (lenbytes * 8)) - 1 + lenbytes;
-}
-
-#ifndef __has_builtin
-#define __has_builtin(x) 0
-#endif
-
-static WPACKET_SUB *wpacket_sub_new(WPACKET *pkt)
-{
-    WPACKET_SUB *sub;
-    uint32_t idx;
-
-    if (pkt->nsubs != 0) {
-#if __has_builtin(__builtin_ffs)
-        idx = __builtin_ffs(pkt->nsubs) - 1;
-#else
-        int snsubs = pkt->nsubs;
-        static const int MultiplyDeBruijnBitPosition[32] = {
-            0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
-            31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
-        };
-
-        /*
-         * (x & -x) isolates the lowest set bit (LSB)
-         * Multiplying hashes that bit uniquely into the upper 5 bits
-         */
-        idx = MultiplyDeBruijnBitPosition[((uint32_t)((snsubs & -snsubs) * 0x077CB531U)) >> 27];
-#endif
-        pkt->nsubs &= (~(1 << idx));
-        sub = &pkt->inline_subs[idx];
-        memset(sub, 0, sizeof(*sub));
-        sub->flags = WPACKET_FLAGS_SUB_INLINE;
-    } else {
-        sub = OPENSSL_zalloc(sizeof(*sub));
-        if (sub == NULL)
-            return NULL;
-    }
-    return sub;
-}
-
-static void wpacket_sub_free(WPACKET *pkt, WPACKET_SUB *sub)
-{
-    if (sub == NULL)
-        return;
-    if (sub->flags & WPACKET_FLAGS_SUB_INLINE) {
-        uintptr_t idx = ((uintptr_t)sub - (uintptr_t)pkt->inline_subs) / sizeof(WPACKET_SUB);
-
-        pkt->nsubs |= (1 << idx);
-        return;
-    }
-    OPENSSL_free(sub);
-}
-
-static int wpacket_intern_init_len(WPACKET *pkt, size_t lenbytes)
-{
-    unsigned char *lenchars;
-
-    pkt->curr = 0;
-    pkt->written = 0;
-    pkt->nsubs = (1 << WPACKET_INLINE_SUBS) - 1;
-
-    if ((pkt->subs = wpacket_sub_new(pkt)) == NULL)
-        return 0;
-
-    if (lenbytes == 0)
-        return 1;
-
-    pkt->subs->pwritten = lenbytes;
-    pkt->subs->lenbytes = lenbytes;
-
-    if (!WPACKET_allocate_bytes(pkt, lenbytes, &lenchars)) {
-        wpacket_sub_free(pkt, pkt->subs);
-        pkt->subs = NULL;
-        return 0;
-    }
-    pkt->subs->packet_len = 0;
-
-    return 1;
+    return WPACKET_sub_reserve_bytes___int(pkt, len, allocbytes, lenbytes);
 }

 int WPACKET_init_static_len(WPACKET *pkt, unsigned char *buf, size_t len,
     size_t lenbytes)
 {
-    size_t max = maxmaxsize(lenbytes);
-
-    /* Internal API, so should not fail */
-    if (!ossl_assert(buf != NULL && len > 0))
-        return 0;
-
-    pkt->staticbuf = buf;
-    pkt->buf = NULL;
-    pkt->maxsize = (max < len) ? max : len;
-    pkt->endfirst = 0;
-
-    return wpacket_intern_init_len(pkt, lenbytes);
+    return WPACKET_init_static_len_int(pkt, buf, len, lenbytes);
 }

 int WPACKET_init_der(WPACKET *pkt, unsigned char *buf, size_t len)
 {
-    /* Internal API, so should not fail */
-    if (!ossl_assert(buf != NULL && len > 0))
-        return 0;
-
-    pkt->staticbuf = buf;
-    pkt->buf = NULL;
-    pkt->maxsize = len;
-    pkt->endfirst = 1;
-
-    return wpacket_intern_init_len(pkt, 0);
+    return WPACKET_init_der_int(pkt, buf, len);
 }

 int WPACKET_init_len(WPACKET *pkt, BUF_MEM *buf, size_t lenbytes)
 {
-    /* Internal API, so should not fail */
-    if (!ossl_assert(buf != NULL))
-        return 0;
-
-    pkt->staticbuf = NULL;
-    pkt->buf = buf;
-    pkt->maxsize = maxmaxsize(lenbytes);
-    pkt->endfirst = 0;
-
-    return wpacket_intern_init_len(pkt, lenbytes);
+    return WPACKET_init_len_int(pkt, buf, lenbytes);
 }

 int WPACKET_init(WPACKET *pkt, BUF_MEM *buf)
 {
-    return WPACKET_init_len(pkt, buf, 0);
+    return WPACKET_init_int(pkt, buf);
 }

 int WPACKET_init_null(WPACKET *pkt, size_t lenbytes)
 {
-    pkt->staticbuf = NULL;
-    pkt->buf = NULL;
-    pkt->maxsize = maxmaxsize(lenbytes);
-    pkt->endfirst = 0;
-
-    return wpacket_intern_init_len(pkt, 0);
+    return WPACKET_init_null_int(pkt, lenbytes);
 }

 int WPACKET_init_null_der(WPACKET *pkt)
 {
-    pkt->staticbuf = NULL;
-    pkt->buf = NULL;
-    pkt->maxsize = SIZE_MAX;
-    pkt->endfirst = 1;
-
-    return wpacket_intern_init_len(pkt, 0);
+    return WPACKET_init_null_der_int(pkt);
 }

 int WPACKET_set_flags(WPACKET *pkt, unsigned int flags)
 {
-    /* Internal API, so should not fail */
-    if (!ossl_assert(pkt->subs != NULL))
-        return 0;
-
-    /*
-     * Preserve the inline flag to prevent freeing
-     */
-    pkt->subs->flags = (pkt->subs->flags & WPACKET_FLAGS_SUB_INLINE) | flags;
-
-    return 1;
-}
-
-/* Store the |value| of length |len| at location |data| */
-static int put_value(unsigned char *data, uint64_t value, size_t len)
-{
-    if (data == NULL)
-        return 1;
-
-    for (data += len - 1; len > 0; len--) {
-        *data = (unsigned char)(value & 0xff);
-        data--;
-        value >>= 8;
-    }
-
-    /* Check whether we could fit the value in the assigned number of bytes */
-    if (value > 0)
-        return 0;
-
-    return 1;
-}
-
-#if !defined OPENSSL_NO_QUIC && !defined FIPS_MODULE
-static int put_quic_value(unsigned char *data, size_t value, size_t len)
-{
-    if (data == NULL)
-        return 1;
-
-    /* Value too large for field. */
-    if (ossl_quic_vlint_encode_len(value) > len)
-        return 0;
-
-    ossl_quic_vlint_encode_n(data, value, (int)len);
-    return 1;
-}
-#endif
-
-/*
- * Internal helper function used by WPACKET_close(), WPACKET_finish() and
- * WPACKET_fill_lengths() to close a sub-packet and write out its length if
- * necessary. If |doclose| is 0 then it goes through the motions of closing
- * (i.e. it fills in all the lengths), but doesn't actually close anything.
- */
-static int wpacket_intern_close(WPACKET *pkt, WPACKET_SUB *sub, int doclose)
-{
-    size_t packlen = pkt->written - sub->pwritten;
-
-    if (packlen == 0
-        && (sub->flags & WPACKET_FLAGS_NON_ZERO_LENGTH) != 0)
-        return 0;
-
-    if (packlen == 0
-        && sub->flags & WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH) {
-        /* We can't handle this case. Return an error */
-        if (!doclose)
-            return 0;
-
-        /* Deallocate any bytes allocated for the length of the WPACKET */
-        if ((pkt->curr - sub->lenbytes) == sub->packet_len) {
-            pkt->written -= sub->lenbytes;
-            pkt->curr -= sub->lenbytes;
-        }
-
-        /* Don't write out the packet length */
-        sub->packet_len = 0;
-        sub->lenbytes = 0;
-    }
-
-    /* Write out the WPACKET length if needed */
-    if (sub->lenbytes > 0) {
-        unsigned char *buf = GETBUF(pkt);
-
-        if (buf != NULL) {
-#if !defined OPENSSL_NO_QUIC && !defined FIPS_MODULE
-            if ((sub->flags & WPACKET_FLAGS_QUIC_VLINT) == 0) {
-                if (!put_value(&buf[sub->packet_len], packlen, sub->lenbytes))
-                    return 0;
-            } else {
-                if (!put_quic_value(&buf[sub->packet_len], packlen, sub->lenbytes))
-                    return 0;
-            }
-#else
-            if (!put_value(&buf[sub->packet_len], packlen, sub->lenbytes))
-                return 0;
-#endif
-        }
-    } else if (pkt->endfirst && sub->parent != NULL
-        && (packlen != 0
-            || (sub->flags
-                   & WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH)
-                == 0)) {
-        size_t tmplen = packlen;
-        size_t numlenbytes = 1;
-
-        while ((tmplen = tmplen >> 8) > 0)
-            numlenbytes++;
-        if (!WPACKET_put_bytes__(pkt, packlen, numlenbytes))
-            return 0;
-        if (packlen > 0x7f) {
-            numlenbytes |= 0x80;
-            if (!WPACKET_put_bytes_u8(pkt, numlenbytes))
-                return 0;
-        }
-    }
-
-    if (doclose) {
-        pkt->subs = sub->parent;
-        wpacket_sub_free(pkt, sub);
-    }
-
-    return 1;
+    return WPACKET_set_flags_int(pkt, flags);
 }

 int WPACKET_fill_lengths(WPACKET *pkt)
 {
-    WPACKET_SUB *sub;
-
-    if (!ossl_assert(pkt->subs != NULL))
-        return 0;
-
-    for (sub = pkt->subs; sub != NULL; sub = sub->parent) {
-        if (!wpacket_intern_close(pkt, sub, 0))
-            return 0;
-    }
-
-    return 1;
+    return WPACKET_fill_lengths_int(pkt);
 }

 int WPACKET_close(WPACKET *pkt)
 {
-    /*
-     * Internal API, so should not fail - but we do negative testing of this
-     * so no assert (otherwise the tests fail)
-     */
-    if (pkt->subs == NULL || pkt->subs->parent == NULL)
-        return 0;
-
-    return wpacket_intern_close(pkt, pkt->subs, 1);
+    return WPACKET_close_int(pkt);
 }

 int WPACKET_finish(WPACKET *pkt)
 {
-    int ret;
-
-    /*
-     * Internal API, so should not fail - but we do negative testing of this
-     * so no assert (otherwise the tests fail)
-     */
-    if (pkt->subs == NULL || pkt->subs->parent != NULL)
-        return 0;
-
-    ret = wpacket_intern_close(pkt, pkt->subs, 1);
-    if (ret) {
-        wpacket_sub_free(pkt, pkt->subs);
-        pkt->subs = NULL;
-    }
-
-    return ret;
+    return WPACKET_finish_int(pkt);
 }

 int WPACKET_start_sub_packet_len__(WPACKET *pkt, size_t lenbytes)
 {
-    return WPACKET_start_sub_packet_at_offset_len__(pkt, lenbytes, 0);
+    return WPACKET_start_sub_packet_len___int(pkt, lenbytes);
 }

 int WPACKET_start_sub_packet_at_offset_len__(WPACKET *pkt, size_t lenbytes,
     size_t offset)
 {
-    WPACKET_SUB *sub;
-    unsigned char *lenchars;
-
-    /* Internal API, so should not fail */
-    if (!ossl_assert(pkt->subs != NULL))
-        return 0;
-
-    /* We don't support lenbytes greater than 0 when doing endfirst writing */
-    if (lenbytes > 0 && pkt->endfirst)
-        return 0;
-
-    if ((sub = wpacket_sub_new(pkt)) == NULL)
-        return 0;
-
-    sub->parent = pkt->subs;
-    pkt->subs = sub;
-    sub->pwritten = pkt->written + lenbytes + offset;
-    sub->lenbytes = lenbytes;
-
-    if (lenbytes == 0) {
-        sub->packet_len = 0;
-        return 1;
-    }
-
-    sub->packet_len = pkt->written;
-
-    if (!WPACKET_allocate_bytes(pkt, lenbytes, &lenchars)
-        || (offset > 0 && !WPACKET_allocate_bytes(pkt, offset, &lenchars)))
-        return 0;
-
-    return 1;
+    return WPACKET_start_sub_packet_at_offset_len___int(pkt, lenbytes, offset);
 }

 int WPACKET_start_sub_packet(WPACKET *pkt)
 {
-    return WPACKET_start_sub_packet_len__(pkt, 0);
+    return WPACKET_start_sub_packet_int(pkt);
 }

 int WPACKET_put_bytes__(WPACKET *pkt, uint64_t val, size_t size)
 {
-    unsigned char *data;
-
-    /* Internal API, so should not fail */
-    if (!ossl_assert(size <= sizeof(uint64_t))
-        || !WPACKET_allocate_bytes(pkt, size, &data)
-        || !put_value(data, val, size))
-        return 0;
-
-    return 1;
+    return WPACKET_put_bytes___int(pkt, val, size);
 }

 int WPACKET_set_max_size(WPACKET *pkt, size_t maxsize)
 {
-    WPACKET_SUB *sub;
-    size_t lenbytes;
-
-    /* Internal API, so should not fail */
-    if (!ossl_assert(pkt->subs != NULL))
-        return 0;
-
-    /* Find the WPACKET_SUB for the top level */
-    for (sub = pkt->subs; sub->parent != NULL; sub = sub->parent)
-        continue;
-
-    lenbytes = sub->lenbytes;
-    if (lenbytes == 0)
-        lenbytes = sizeof(pkt->maxsize);
-
-    if (maxmaxsize(lenbytes) < maxsize || maxsize < pkt->written)
-        return 0;
-
-    pkt->maxsize = maxsize;
-
-    return 1;
+    return WPACKET_set_max_size_int(pkt, maxsize);
 }

 int WPACKET_memset(WPACKET *pkt, int ch, size_t len)
 {
-    unsigned char *dest;
-
-    if (len == 0)
-        return 1;
-
-    if (!WPACKET_allocate_bytes(pkt, len, &dest))
-        return 0;
-
-    if (dest != NULL)
-        memset(dest, ch, len);
-
-    return 1;
+    return WPACKET_memset_int(pkt, ch, len);
 }

 int WPACKET_memcpy(WPACKET *pkt, const void *src, size_t len)
 {
-    unsigned char *dest;
-
-    if (len == 0)
-        return 1;
-
-    if (!WPACKET_allocate_bytes(pkt, len, &dest))
-        return 0;
-
-    if (dest != NULL)
-        memcpy(dest, src, len);
-
-    return 1;
+    return WPACKET_memcpy_int(pkt, src, len);
 }

 int WPACKET_sub_memcpy__(WPACKET *pkt, const void *src, size_t len,
     size_t lenbytes)
 {
-    if (!WPACKET_start_sub_packet_len__(pkt, lenbytes)
-        || !WPACKET_memcpy(pkt, src, len)
-        || !WPACKET_close(pkt))
-        return 0;
-
-    return 1;
+    return WPACKET_sub_memcpy___int(pkt, src, len, lenbytes);
 }

 int WPACKET_get_total_written(WPACKET *pkt, size_t *written)
 {
-    /* Internal API, so should not fail */
-    if (!ossl_assert(written != NULL))
-        return 0;
-
-    *written = pkt->written;
-
-    return 1;
+    return WPACKET_get_total_written_int(pkt, written);
 }

 int WPACKET_get_length(WPACKET *pkt, size_t *len)
 {
-    /* Internal API, so should not fail */
-    if (!ossl_assert(pkt->subs != NULL && len != NULL))
-        return 0;
-
-    *len = pkt->written - pkt->subs->pwritten;
-
-    return 1;
+    return WPACKET_get_length_int(pkt, len);
 }

 unsigned char *WPACKET_get_curr(WPACKET *pkt)
 {
-    unsigned char *buf = GETBUF(pkt);
-
-    if (buf == NULL)
-        return NULL;
-
-    if (pkt->endfirst)
-        return buf + pkt->maxsize - pkt->curr;
-
-    return buf + pkt->curr;
+    return WPACKET_get_curr_int(pkt);
 }

 int WPACKET_is_null_buf(WPACKET *pkt)
 {
-    return pkt->buf == NULL && pkt->staticbuf == NULL;
+    return WPACKET_is_null_buf_int(pkt);
 }

 void WPACKET_cleanup(WPACKET *pkt)
 {
-    WPACKET_SUB *sub, *parent;
-
-    for (sub = pkt->subs; sub != NULL; sub = parent) {
-        parent = sub->parent;
-        wpacket_sub_free(pkt, sub);
-    }
-    pkt->subs = NULL;
+    WPACKET_cleanup_int(pkt);
 }

+#endif
+
 #if !defined OPENSSL_NO_QUIC && !defined FIPS_MODULE

 int WPACKET_start_quic_sub_packet_bound(WPACKET *pkt, size_t max_len)
diff --git a/include/internal/packet.h b/include/internal/packet.h
index c7628c068f..d1efff3cee 100644
--- a/include/internal/packet.h
+++ b/include/internal/packet.h
@@ -18,6 +18,7 @@
 #include <openssl/e_os2.h>

 #include "internal/numbers.h"
+#include "internal/packet_inline.h"

 typedef struct {
     /* Pointer to where we are currently reading from */
@@ -689,78 +690,24 @@ __owur static ossl_inline int PACKET_get_length_prefixed_3(PACKET *pkt,
     return 1;
 }

-/* Writable packets */
-
-#define WPACKET_INLINE_SUBS 6
-typedef struct wpacket_sub WPACKET_SUB;
-struct wpacket_sub {
-    /* The parent WPACKET_SUB if we have one or NULL otherwise */
-    WPACKET_SUB *parent;
-
-    /*
-     * Offset into the buffer where the length of this WPACKET goes. We use an
-     * offset in case the buffer grows and gets reallocated.
-     */
-    size_t packet_len;
-
-    /* Number of bytes in the packet_len or 0 if we don't write the length */
-    size_t lenbytes;
-
-    /* Number of bytes written to the buf prior to this packet starting */
-    size_t pwritten;
-
-    /* Flags for this sub-packet */
-    unsigned int flags;
-};
-
-typedef struct wpacket_st WPACKET;
-struct wpacket_st {
-    /* The buffer where we store the output data */
-    BUF_MEM *buf;
-
-    /* Fixed sized buffer which can be used as an alternative to buf */
-    unsigned char *staticbuf;
-
-    /*
-     * Offset into the buffer where we are currently writing. We use an offset
-     * in case the buffer grows and gets reallocated.
-     */
-    size_t curr;
-
-    /* Number of bytes written so far */
-    size_t written;
-
-    /* Maximum number of bytes we will allow to be written to this WPACKET */
-    size_t maxsize;
-
-    /* Our sub-packets (always at least one if not finished) */
-    WPACKET_SUB *subs;
-
-    WPACKET_SUB inline_subs[WPACKET_INLINE_SUBS];
-    unsigned int nsubs;
-
-    /* Writing from the end first? */
-    unsigned int endfirst : 1;
-};
-
-/* Flags */
-
-/* Default */
-#define WPACKET_FLAGS_NONE 0
-
-/* Error on WPACKET_close() if no data written to the WPACKET */
-#define WPACKET_FLAGS_NON_ZERO_LENGTH 1
-
-/*
- * Abandon all changes on WPACKET_close() if no data written to the WPACKET,
- * i.e. this does not write out a zero packet length
- */
-#define WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH 2
-
-/* QUIC variable-length integer length prefix */
-#define WPACKET_FLAGS_QUIC_VLINT 4
-
-#define WPACKET_FLAGS_SUB_INLINE 8
+#ifdef OPENSSL_SMALL_FOOTPRINT
+#define DECLARE_WPACKET_API1(rettype, name, t1, a1) rettype name(t1 a1);
+#define DECLARE_WPACKET_API1_VOID(name, t1, a1) void name(t1 a1);
+#define DECLARE_WPACKET_API2(rettype, name, t1, a1, t2, a2) rettype name(t1 a1, t2 a2);
+#define DECLARE_WPACKET_API3(rettype, name, t1, a1, t2, a2, t3, a3) rettype name(t1 a1, t2 a2, t3 a3);
+#define DECLARE_WPACKET_API4(rettype, name, t1, a1, t2, a2, t3, a3, t4, a4) rettype name(t1 a1, t2 a2, t3 a3, t4 a4);
+#else
+#define DECLARE_WPACKET_API1(rettype, name, t1, a1) \
+    static ossl_inline rettype name(t1 a1) { return name##_int(a1); }
+#define DECLARE_WPACKET_API1_VOID(name, t1, a1) \
+    static ossl_inline void name(t1 a1) { name##_int(a1); }
+#define DECLARE_WPACKET_API2(rettype, name, t1, a1, t2, a2) \
+    static ossl_inline rettype name(t1 a1, t2 a2) { return name##_int(a1, a2); }
+#define DECLARE_WPACKET_API3(rettype, name, t1, a1, t2, a2, t3, a3) \
+    static ossl_inline rettype name(t1 a1, t2 a2, t3 a3) { return name##_int(a1, a2, a3); }
+#define DECLARE_WPACKET_API4(rettype, name, t1, a1, t2, a2, t3, a3, t4, a4) \
+    static ossl_inline rettype name(t1 a1, t2 a2, t3 a3, t4 a4) { return name##_int(a1, a2, a3, a4); }
+#endif

 /*
  * Initialise a WPACKET with the buffer in |buf|. The buffer must exist
@@ -768,46 +715,45 @@ struct wpacket_st {
  * data is preallocated at the start of the buffer to store the length of the
  * WPACKET once we know it.
  */
-int WPACKET_init_len(WPACKET *pkt, BUF_MEM *buf, size_t lenbytes);
+DECLARE_WPACKET_API3(int, WPACKET_init_len, WPACKET *, pkt, BUF_MEM *, buf, size_t, lenbytes)

 /*
  * Same as WPACKET_init_len except there is no preallocation of the WPACKET
  * length.
  */
-int WPACKET_init(WPACKET *pkt, BUF_MEM *buf);
+DECLARE_WPACKET_API2(int, WPACKET_init, WPACKET *, pkt, BUF_MEM *, buf)

 /*
  * Same as WPACKET_init_len except there is no underlying buffer. No data is
  * ever actually written. We just keep track of how much data would have been
  * written if a buffer was there.
  */
-int WPACKET_init_null(WPACKET *pkt, size_t lenbytes);
+DECLARE_WPACKET_API2(int, WPACKET_init_null, WPACKET *, pkt, size_t, lenbytes)

 /*
  * Same as WPACKET_init_null except we set the WPACKET to assume DER length
  * encoding for sub-packets.
  */
-int WPACKET_init_null_der(WPACKET *pkt);
+DECLARE_WPACKET_API1(int, WPACKET_init_null_der, WPACKET *, pkt)

 /*
  * Same as WPACKET_init_len except we do not use a growable BUF_MEM structure.
  * A fixed buffer of memory |buf| of size |len| is used instead. A failure will
  * occur if you attempt to write beyond the end of the buffer
  */
-int WPACKET_init_static_len(WPACKET *pkt, unsigned char *buf, size_t len,
-    size_t lenbytes);
+DECLARE_WPACKET_API4(int, WPACKET_init_static_len, WPACKET *, pkt, unsigned char *, buf, size_t, len, size_t, lenbytes)

 /*
  * Same as WPACKET_init_static_len except lenbytes is always 0, and we set the
  * WPACKET to write to the end of the buffer moving towards the start and use
  * DER length encoding for sub-packets.
  */
-int WPACKET_init_der(WPACKET *pkt, unsigned char *buf, size_t len);
+DECLARE_WPACKET_API3(int, WPACKET_init_der, WPACKET *, pkt, unsigned char *, buf, size_t, len)

 /*
  * Set the flags to be applied to the current sub-packet
  */
-int WPACKET_set_flags(WPACKET *pkt, unsigned int flags);
+DECLARE_WPACKET_API2(int, WPACKET_set_flags, WPACKET *, pkt, unsigned int, flags)

 /*
  * Closes the most recent sub-packet. It also writes out the length of the
@@ -815,13 +761,13 @@ int WPACKET_set_flags(WPACKET *pkt, unsigned int flags);
  * appropriate. The top level WPACKET should be closed using WPACKET_finish()
  * instead of this function.
  */
-int WPACKET_close(WPACKET *pkt);
+DECLARE_WPACKET_API1(int, WPACKET_close, WPACKET *, pkt)

 /*
  * The same as WPACKET_close() but only for the top most WPACKET. Additionally
  * frees memory resources for this WPACKET.
  */
-int WPACKET_finish(WPACKET *pkt);
+DECLARE_WPACKET_API1(int, WPACKET_finish, WPACKET *, pkt)

 /*
  * Iterate through all the sub-packets and write out their lengths as if they
@@ -830,7 +776,7 @@ int WPACKET_finish(WPACKET *pkt);
  * data is added to the WPACKET). This function fails if a sub-packet is of 0
  * length and WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH is set.
  */
-int WPACKET_fill_lengths(WPACKET *pkt);
+DECLARE_WPACKET_API1(int, WPACKET_fill_lengths, WPACKET *, pkt)

 /*
  * Initialise a new sub-packet. Additionally |lenbytes| of data is preallocated
@@ -838,8 +784,7 @@ int WPACKET_fill_lengths(WPACKET *pkt);
  * it. The sub-packet start is set at |offset| from current position in |pkt|
  * Don't call this directly. Use the convenience macros below instead.
  */
-int WPACKET_start_sub_packet_at_offset_len__(WPACKET *pkt, size_t lenbytes,
-    size_t offset);
+DECLARE_WPACKET_API3(int, WPACKET_start_sub_packet_at_offset_len__, WPACKET *, pkt, size_t, lenbytes, size_t, offset)

 #define WPACKET_start_sub_packet_u24_at_offset(pkt, offset) \
     WPACKET_start_sub_packet_at_offset_len__((pkt), 3, (offset))
@@ -849,7 +794,7 @@ int WPACKET_start_sub_packet_at_offset_len__(WPACKET *pkt, size_t lenbytes,
  * at the start of the sub-packet to store its length once we know it. Don't
  * call this directly. Use the convenience macros below instead.
  */
-int WPACKET_start_sub_packet_len__(WPACKET *pkt, size_t lenbytes);
+DECLARE_WPACKET_API2(int, WPACKET_start_sub_packet_len__, WPACKET *, pkt, size_t, lenbytes)

 /*
  * Convenience macros for calling WPACKET_start_sub_packet_len with different
@@ -868,7 +813,7 @@ int WPACKET_start_sub_packet_len__(WPACKET *pkt, size_t lenbytes);
  * Same as WPACKET_start_sub_packet_len__() except no bytes are pre-allocated
  * for the sub-packet length.
  */
-int WPACKET_start_sub_packet(WPACKET *pkt);
+DECLARE_WPACKET_API1(int, WPACKET_start_sub_packet, WPACKET *, pkt)

 /*
  * Allocate bytes in the WPACKET for the output. This reserves the bytes
@@ -878,8 +823,7 @@ int WPACKET_start_sub_packet(WPACKET *pkt);
  * WPACKET_* calls. If not then the underlying buffer may be realloc'd and
  * change its location.
  */
-int WPACKET_allocate_bytes(WPACKET *pkt, size_t len,
-    unsigned char **allocbytes);
+DECLARE_WPACKET_API3(int, WPACKET_allocate_bytes, WPACKET *, pkt, size_t, len, unsigned char **, allocbytes)

 /*
  * The same as WPACKET_allocate_bytes() except additionally a new sub-packet is
@@ -887,8 +831,7 @@ int WPACKET_allocate_bytes(WPACKET *pkt, size_t len,
  * number of length bytes for the sub-packet is in |lenbytes|. Don't call this
  * directly. Use the convenience macros below instead.
  */
-int WPACKET_sub_allocate_bytes__(WPACKET *pkt, size_t len,
-    unsigned char **allocbytes, size_t lenbytes);
+DECLARE_WPACKET_API4(int, WPACKET_sub_allocate_bytes__, WPACKET *, pkt, size_t, len, unsigned char **, allocbytes, size_t, lenbytes)

 /*
  * Convenience macros for calling WPACKET_sub_allocate_bytes with different
@@ -921,13 +864,12 @@ int WPACKET_sub_allocate_bytes__(WPACKET *pkt, size_t len,
  *          || sigbytes1 != sigbytes2)
  *      goto err;
  */
-int WPACKET_reserve_bytes(WPACKET *pkt, size_t len, unsigned char **allocbytes);
+DECLARE_WPACKET_API3(int, WPACKET_reserve_bytes, WPACKET *, pkt, size_t, len, unsigned char **, allocbytes)

 /*
  * The "reserve_bytes" equivalent of WPACKET_sub_allocate_bytes__()
  */
-int WPACKET_sub_reserve_bytes__(WPACKET *pkt, size_t len,
-    unsigned char **allocbytes, size_t lenbytes);
+DECLARE_WPACKET_API4(int, WPACKET_sub_reserve_bytes__, WPACKET *, pkt, size_t, len, unsigned char **, allocbytes, size_t, lenbytes)

 /*
  * Convenience macros for  WPACKET_sub_reserve_bytes with different lengths
@@ -941,15 +883,6 @@ int WPACKET_sub_reserve_bytes__(WPACKET *pkt, size_t len,
 #define WPACKET_sub_reserve_bytes_u32(pkt, len, bytes) \
     WPACKET_sub_reserve_bytes__((pkt), (len), (bytes), 4)

-/*
- * Write the value stored in |val| into the WPACKET. The value will consume
- * |bytes| amount of storage. An error will occur if |val| cannot be
- * accommodated in |bytes| storage, e.g. attempting to write the value 256 into
- * 1 byte will fail. Don't call this directly. Use the convenience macros below
- * instead.
- */
-int WPACKET_put_bytes__(WPACKET *pkt, uint64_t val, size_t bytes);
-
 /*
  * Convenience macros for calling WPACKET_put_bytes with different
  * lengths
@@ -967,22 +900,30 @@ int WPACKET_put_bytes__(WPACKET *pkt, uint64_t val, size_t bytes);
 #define WPACKET_put_bytes_u64(pkt, val) \
     WPACKET_put_bytes__((pkt), (val), 8)

+/*
+ * Write the value stored in |val| into the WPACKET. The value will consume
+ * |bytes| amount of storage. An error will occur if |val| cannot be
+ * accommodated in |bytes| storage, e.g. attempting to write the value 256 into
+ * 1 byte will fail. Don't call this directly. Use the convenience macros below
+ * instead.
+ */
+DECLARE_WPACKET_API3(int, WPACKET_put_bytes__, WPACKET *, pkt, uint64_t, val, size_t, bytes)
+
 /* Set a maximum size that we will not allow the WPACKET to grow beyond */
-int WPACKET_set_max_size(WPACKET *pkt, size_t maxsize);
+DECLARE_WPACKET_API2(int, WPACKET_set_max_size, WPACKET *, pkt, size_t, maxsize)

 /* Copy |len| bytes of data from |*src| into the WPACKET. */
-int WPACKET_memcpy(WPACKET *pkt, const void *src, size_t len);
+DECLARE_WPACKET_API3(int, WPACKET_memcpy, WPACKET *, pkt, const void *, src, size_t, len)

 /* Set |len| bytes of data to |ch| into the WPACKET. */
-int WPACKET_memset(WPACKET *pkt, int ch, size_t len);
+DECLARE_WPACKET_API3(int, WPACKET_memset, WPACKET *, pkt, int, ch, size_t, len)

 /*
  * Copy |len| bytes of data from |*src| into the WPACKET and prefix with its
  * length (consuming |lenbytes| of data for the length). Don't call this
  * directly. Use the convenience macros below instead.
  */
-int WPACKET_sub_memcpy__(WPACKET *pkt, const void *src, size_t len,
-    size_t lenbytes);
+DECLARE_WPACKET_API4(int, WPACKET_sub_memcpy__, WPACKET *, pkt, const void *, src, size_t, len, size_t, lenbytes)

 /* Convenience macros for calling WPACKET_sub_memcpy with different lengths */
 #define WPACKET_sub_memcpy_u8(pkt, src, len) \
@@ -998,24 +939,24 @@ int WPACKET_sub_memcpy__(WPACKET *pkt, const void *src, size_t len,
  * Return the total number of bytes written so far to the underlying buffer
  * including any storage allocated for length bytes
  */
-int WPACKET_get_total_written(WPACKET *pkt, size_t *written);
+DECLARE_WPACKET_API2(int, WPACKET_get_total_written, WPACKET *, pkt, size_t *, written)

 /*
  * Returns the length of the current sub-packet. This excludes any bytes
  * allocated for the length itself.
  */
-int WPACKET_get_length(WPACKET *pkt, size_t *len);
+DECLARE_WPACKET_API2(int, WPACKET_get_length, WPACKET *, pkt, size_t *, len)

 /*
  * Returns a pointer to the current write location, but does not allocate any
  * bytes.
  */
-unsigned char *WPACKET_get_curr(WPACKET *pkt);
+DECLARE_WPACKET_API1(unsigned char *, WPACKET_get_curr, WPACKET *, pkt)

 /* Returns true if the underlying buffer is actually NULL */
-int WPACKET_is_null_buf(WPACKET *pkt);
+DECLARE_WPACKET_API1(int, WPACKET_is_null_buf, WPACKET *, pkt)

 /* Release resources in a WPACKET if a failure has occurred. */
-void WPACKET_cleanup(WPACKET *pkt);
+DECLARE_WPACKET_API1_VOID(WPACKET_cleanup, WPACKET *, pkt)

 #endif /* OSSL_INTERNAL_PACKET_H */
diff --git a/include/internal/packet_inline.h b/include/internal/packet_inline.h
new file mode 100644
index 0000000000..645c7e5d6d
--- /dev/null
+++ b/include/internal/packet_inline.h
@@ -0,0 +1,687 @@
+/*
+ * Copyright 2015-2026 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_INTERNAL_PACKET_INLINE_H
+#define OSSL_INTERNAL_PACKET_INLINE_H
+
+#include <string.h>
+#include <openssl/bn.h>
+#include <openssl/buffer.h>
+#include <openssl/crypto.h>
+#include <openssl/e_os2.h>
+
+#include "internal/numbers.h"
+#include "internal/common.h"
+#if !defined OPENSSL_NO_QUIC && !defined FIPS_MODULE
+#include "internal/quic_vlint.h"
+#endif
+
+#define DEFAULT_BUF_SIZE 256
+
+/* Writable packets */
+
+#define WPACKET_INLINE_SUBS 6
+typedef struct wpacket_sub WPACKET_SUB;
+struct wpacket_sub {
+    /* The parent WPACKET_SUB if we have one or NULL otherwise */
+    WPACKET_SUB *parent;
+
+    /*
+     * Offset into the buffer where the length of this WPACKET goes. We use an
+     * offset in case the buffer grows and gets reallocated.
+     */
+    size_t packet_len;
+
+    /* Number of bytes in the packet_len or 0 if we don't write the length */
+    size_t lenbytes;
+
+    /* Number of bytes written to the buf prior to this packet starting */
+    size_t pwritten;
+
+    /* Flags for this sub-packet */
+    unsigned int flags;
+};
+
+typedef struct wpacket_st WPACKET;
+struct wpacket_st {
+    /* The buffer where we store the output data */
+    BUF_MEM *buf;
+
+    /* Fixed sized buffer which can be used as an alternative to buf */
+    unsigned char *staticbuf;
+
+    /*
+     * Offset into the buffer where we are currently writing. We use an offset
+     * in case the buffer grows and gets reallocated.
+     */
+    size_t curr;
+
+    /* Number of bytes written so far */
+    size_t written;
+
+    /* Maximum number of bytes we will allow to be written to this WPACKET */
+    size_t maxsize;
+
+    /* Our sub-packets (always at least one if not finished) */
+    WPACKET_SUB *subs;
+
+    WPACKET_SUB inline_subs[WPACKET_INLINE_SUBS];
+    unsigned int nsubs;
+
+    /* Writing from the end first? */
+    unsigned int endfirst : 1;
+};
+/* Flags */
+
+/* Default */
+#define WPACKET_FLAGS_NONE 0
+
+/* Error on WPACKET_close() if no data written to the WPACKET */
+#define WPACKET_FLAGS_NON_ZERO_LENGTH 1
+
+/*
+ * Abandon all changes on WPACKET_close() if no data written to the WPACKET,
+ * i.e. this does not write out a zero packet length
+ */
+#define WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH 2
+
+/* QUIC variable-length integer length prefix */
+#define WPACKET_FLAGS_QUIC_VLINT 4
+
+#define WPACKET_FLAGS_SUB_INLINE 8
+
+#define GETBUF(p) (((p)->staticbuf != NULL)         \
+        ? (p)->staticbuf                            \
+        : ((p)->buf != NULL                         \
+                  ? (unsigned char *)(p)->buf->data \
+                  : NULL))
+
+static ossl_inline ossl_unused size_t maxmaxsize(size_t lenbytes)
+{
+    if (lenbytes >= sizeof(size_t) || lenbytes == 0)
+        return SIZE_MAX;
+
+    return ((size_t)1 << (lenbytes * 8)) - 1 + lenbytes;
+}
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+static ossl_inline ossl_unused WPACKET_SUB *wpacket_sub_new(WPACKET *pkt)
+{
+    WPACKET_SUB *sub;
+    uint32_t idx;
+
+    if (pkt->nsubs != 0) {
+#if __has_builtin(__builtin_ffs)
+        idx = __builtin_ffs(pkt->nsubs) - 1;
+#else
+        int snsubs = pkt->nsubs;
+        static const int MultiplyDeBruijnBitPosition[32] = {
+            0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+            31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+        };
+
+        /*
+         * (x & -x) isolates the lowest set bit (LSB)
+         * Multiplying hashes that bit uniquely into the upper 5 bits
+         */
+        idx = MultiplyDeBruijnBitPosition[((uint32_t)((snsubs & -snsubs) * 0x077CB531U)) >> 27];
+#endif
+        pkt->nsubs &= (~(1 << idx));
+        sub = &pkt->inline_subs[idx];
+        memset(sub, 0, sizeof(*sub));
+        sub->flags = WPACKET_FLAGS_SUB_INLINE;
+    } else {
+        sub = OPENSSL_zalloc(sizeof(*sub));
+        if (sub == NULL)
+            return NULL;
+    }
+    return sub;
+}
+
+static ossl_inline ossl_unused void wpacket_sub_free(WPACKET *pkt, WPACKET_SUB *sub)
+{
+    if (sub == NULL)
+        return;
+    if (sub->flags & WPACKET_FLAGS_SUB_INLINE) {
+        uintptr_t idx = ((uintptr_t)sub - (uintptr_t)pkt->inline_subs) / sizeof(WPACKET_SUB);
+
+        pkt->nsubs |= (1 << idx);
+        return;
+    }
+    OPENSSL_free(sub);
+}
+
+static ossl_inline ossl_unused unsigned char *WPACKET_get_curr_int(WPACKET *pkt)
+{
+    unsigned char *buf = GETBUF(pkt);
+
+    if (buf == NULL)
+        return NULL;
+
+    if (pkt->endfirst)
+        return buf + pkt->maxsize - pkt->curr;
+
+    return buf + pkt->curr;
+}
+
+static ossl_inline ossl_unused int WPACKET_reserve_bytes_int(WPACKET *pkt, size_t len, unsigned char **allocbytes)
+{
+    /* Internal API, so should not fail */
+    if (!ossl_assert(pkt->subs != NULL && len != 0))
+        return 0;
+
+    if (pkt->maxsize - pkt->written < len)
+        return 0;
+
+    if (pkt->buf != NULL && (pkt->buf->length - pkt->written < len)) {
+        size_t newlen;
+        size_t reflen;
+
+        reflen = (len > pkt->buf->length) ? len : pkt->buf->length;
+
+        if (reflen > SIZE_MAX / 2) {
+            newlen = SIZE_MAX;
+        } else {
+            newlen = reflen * 2;
+            if (newlen < DEFAULT_BUF_SIZE)
+                newlen = DEFAULT_BUF_SIZE;
+        }
+        if (BUF_MEM_grow(pkt->buf, newlen) == 0)
+            return 0;
+    }
+    if (allocbytes != NULL) {
+        *allocbytes = WPACKET_get_curr_int(pkt);
+        if (pkt->endfirst && *allocbytes != NULL)
+            *allocbytes -= len;
+    }
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_allocate_bytes_int(WPACKET *pkt, size_t len, unsigned char **allocbytes)
+{
+    if (!WPACKET_reserve_bytes_int(pkt, len, allocbytes))
+        return 0;
+
+    pkt->written += len;
+    pkt->curr += len;
+    return 1;
+}
+
+static ossl_inline ossl_unused int wpacket_intern_init_len(WPACKET *pkt, size_t lenbytes)
+{
+    unsigned char *lenchars;
+
+    pkt->curr = 0;
+    pkt->written = 0;
+    pkt->nsubs = (1 << WPACKET_INLINE_SUBS) - 1;
+
+    if ((pkt->subs = wpacket_sub_new(pkt)) == NULL)
+        return 0;
+
+    if (lenbytes == 0)
+        return 1;
+
+    pkt->subs->pwritten = lenbytes;
+    pkt->subs->lenbytes = lenbytes;
+
+    if (!WPACKET_allocate_bytes_int(pkt, lenbytes, &lenchars)) {
+        wpacket_sub_free(pkt, pkt->subs);
+        pkt->subs = NULL;
+        return 0;
+    }
+    pkt->subs->packet_len = 0;
+
+    return 1;
+}
+
+/*
+ * Initialise a WPACKET with the buffer in |buf|. The buffer must exist
+ * for the whole time that the WPACKET is being used. Additionally |lenbytes| of
+ * data is preallocated at the start of the buffer to store the length of the
+ * WPACKET once we know it.
+ */
+static ossl_inline ossl_unused int WPACKET_init_len_int(WPACKET *pkt, BUF_MEM *buf, size_t lenbytes)
+{
+    /* Internal API, so should not fail */
+    if (!ossl_assert(buf != NULL))
+        return 0;
+
+    pkt->staticbuf = NULL;
+    pkt->buf = buf;
+    pkt->maxsize = maxmaxsize(lenbytes);
+    pkt->endfirst = 0;
+
+    return wpacket_intern_init_len(pkt, lenbytes);
+}
+
+static ossl_inline ossl_unused int WPACKET_init_int(WPACKET *pkt, BUF_MEM *buf)
+{
+    return WPACKET_init_len_int(pkt, buf, 0);
+}
+
+static ossl_inline ossl_unused int WPACKET_init_null_der_int(WPACKET *pkt)
+{
+    pkt->staticbuf = NULL;
+    pkt->buf = NULL;
+    pkt->maxsize = SIZE_MAX;
+    pkt->endfirst = 1;
+
+    return wpacket_intern_init_len(pkt, 0);
+}
+
+static ossl_inline ossl_unused int WPACKET_init_null_int(WPACKET *pkt, size_t lenbytes)
+{
+    pkt->staticbuf = NULL;
+    pkt->buf = NULL;
+    pkt->maxsize = maxmaxsize(lenbytes);
+    pkt->endfirst = 0;
+
+    return wpacket_intern_init_len(pkt, 0);
+}
+
+static ossl_inline ossl_unused int WPACKET_init_der_int(WPACKET *pkt, unsigned char *buf, size_t len)
+{
+    /* Internal API, so should not fail */
+    if (!ossl_assert(buf != NULL && len > 0))
+        return 0;
+
+    pkt->staticbuf = buf;
+    pkt->buf = NULL;
+    pkt->maxsize = len;
+    pkt->endfirst = 1;
+
+    return wpacket_intern_init_len(pkt, 0);
+}
+
+static ossl_inline ossl_unused int WPACKET_init_static_len_int(WPACKET *pkt, unsigned char *buf, size_t len,
+    size_t lenbytes)
+{
+    size_t max = maxmaxsize(lenbytes);
+
+    pkt->staticbuf = buf;
+    pkt->buf = NULL;
+    pkt->maxsize = (max < len) ? max : len;
+    pkt->endfirst = 0;
+    pkt->subs = NULL;
+
+    /* Internal API, so should not fail */
+    if (!ossl_assert(buf != NULL && len > 0))
+        return 0;
+
+    return wpacket_intern_init_len(pkt, lenbytes);
+}
+
+static ossl_inline ossl_unused int WPACKET_memset_int(WPACKET *pkt, int ch, size_t len)
+{
+    unsigned char *dest;
+
+    if (len == 0)
+        return 1;
+
+    if (!WPACKET_allocate_bytes_int(pkt, len, &dest))
+        return 0;
+
+    if (dest != NULL)
+        memset(dest, ch, len);
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_sub_reserve_bytes___int(WPACKET *pkt, size_t len,
+    unsigned char **allocbytes, size_t lenbytes)
+{
+    if (pkt->endfirst && lenbytes > 0)
+        return 0;
+
+    if (!WPACKET_reserve_bytes_int(pkt, lenbytes + len, allocbytes))
+        return 0;
+
+    if (*allocbytes != NULL)
+        *allocbytes += lenbytes;
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_start_sub_packet_at_offset_len___int(WPACKET *pkt, size_t lenbytes,
+    size_t offset)
+{
+    WPACKET_SUB *sub;
+    unsigned char *lenchars;
+
+    /* Internal API, so should not fail */
+    if (!ossl_assert(pkt->subs != NULL))
+        return 0;
+
+    /* We don't support lenbytes greater than 0 when doing endfirst writing */
+    if (lenbytes > 0 && pkt->endfirst)
+        return 0;
+
+    if ((sub = wpacket_sub_new(pkt)) == NULL)
+        return 0;
+
+    sub->parent = pkt->subs;
+    pkt->subs = sub;
+    sub->pwritten = pkt->written + lenbytes + offset;
+    sub->lenbytes = lenbytes;
+
+    if (lenbytes == 0) {
+        sub->packet_len = 0;
+        return 1;
+    }
+
+    sub->packet_len = pkt->written;
+
+    if (!WPACKET_allocate_bytes_int(pkt, lenbytes, &lenchars)
+        || (offset > 0 && !WPACKET_allocate_bytes_int(pkt, offset, &lenchars)))
+        return 0;
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_start_sub_packet_len___int(WPACKET *pkt, size_t lenbytes)
+{
+    return WPACKET_start_sub_packet_at_offset_len___int(pkt, lenbytes, 0);
+}
+
+static ossl_inline ossl_unused int WPACKET_start_sub_packet_int(WPACKET *pkt)
+{
+    return WPACKET_start_sub_packet_len___int(pkt, 0);
+}
+
+#if !defined OPENSSL_NO_QUIC && !defined FIPS_MODULE
+static ossl_inline ossl_unused int put_quic_value(unsigned char *data, size_t value, size_t len)
+{
+    if (data == NULL)
+        return 1;
+
+    /* Value too large for field. */
+    if (ossl_quic_vlint_encode_len(value) > len)
+        return 0;
+
+    ossl_quic_vlint_encode_n(data, value, (int)len);
+    return 1;
+}
+#endif
+
+/* Store the |value| of length |len| at location |data| */
+static ossl_inline ossl_unused int put_value(unsigned char *data, uint64_t value, size_t len)
+{
+    if (data == NULL)
+        return 1;
+
+    for (data += len - 1; len > 0; len--) {
+        *data = (unsigned char)(value & 0xff);
+        data--;
+        value >>= 8;
+    }
+
+    /* Check whether we could fit the value in the assigned number of bytes */
+    if (value > 0)
+        return 0;
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_put_bytes___int(WPACKET *pkt, uint64_t val, size_t size)
+{
+    unsigned char *data;
+
+    /* Internal API, so should not fail */
+    if (!ossl_assert(size <= sizeof(uint64_t))
+        || !WPACKET_allocate_bytes_int(pkt, size, &data)
+        || !put_value(data, val, size))
+        return 0;
+
+    return 1;
+}
+
+/*
+ * Internal helper function used by WPACKET_close(), WPACKET_finish() and
+ * WPACKET_fill_lengths() to close a sub-packet and write out its length if
+ * necessary. If |doclose| is 0 then it goes through the motions of closing
+ * (i.e. it fills in all the lengths), but doesn't actually close anything.
+ */
+static ossl_inline ossl_unused int wpacket_intern_close(WPACKET *pkt, WPACKET_SUB *sub, int doclose)
+{
+    size_t packlen = pkt->written - sub->pwritten;
+
+    if (packlen == 0
+        && (sub->flags & WPACKET_FLAGS_NON_ZERO_LENGTH) != 0)
+        return 0;
+
+    if (packlen == 0
+        && sub->flags & WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH) {
+        /* We can't handle this case. Return an error */
+        if (!doclose)
+            return 0;
+
+        /* Deallocate any bytes allocated for the length of the WPACKET */
+        if ((pkt->curr - sub->lenbytes) == sub->packet_len) {
+            pkt->written -= sub->lenbytes;
+            pkt->curr -= sub->lenbytes;
+        }
+
+        /* Don't write out the packet length */
+        sub->packet_len = 0;
+        sub->lenbytes = 0;
+    }
+
+    /* Write out the WPACKET length if needed */
+    if (sub->lenbytes > 0) {
+        unsigned char *buf = GETBUF(pkt);
+
+        if (buf != NULL) {
+#if !defined OPENSSL_NO_QUIC && !defined FIPS_MODULE
+            if ((sub->flags & WPACKET_FLAGS_QUIC_VLINT) == 0) {
+                if (!put_value(&buf[sub->packet_len], packlen, sub->lenbytes))
+                    return 0;
+            } else {
+                if (!put_quic_value(&buf[sub->packet_len], packlen, sub->lenbytes))
+                    return 0;
+            }
+#else
+            if (!put_value(&buf[sub->packet_len], packlen, sub->lenbytes))
+                return 0;
+#endif
+        }
+    } else if (pkt->endfirst && sub->parent != NULL
+        && (packlen != 0
+            || (sub->flags
+                   & WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH)
+                == 0)) {
+        size_t tmplen = packlen;
+        size_t numlenbytes = 1;
+
+        while ((tmplen = tmplen >> 8) > 0)
+            numlenbytes++;
+        if (!WPACKET_put_bytes___int(pkt, packlen, numlenbytes))
+            return 0;
+        if (packlen > 0x7f) {
+            numlenbytes |= 0x80;
+            /* WPACKET_put_bytes_u8 */
+            if (!WPACKET_put_bytes___int(pkt, numlenbytes, 1))
+                return 0;
+        }
+    }
+
+    if (doclose) {
+        pkt->subs = sub->parent;
+        wpacket_sub_free(pkt, sub);
+    }
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_fill_lengths_int(WPACKET *pkt)
+{
+    WPACKET_SUB *sub;
+
+    if (!ossl_assert(pkt->subs != NULL))
+        return 0;
+
+    for (sub = pkt->subs; sub != NULL; sub = sub->parent) {
+        if (!wpacket_intern_close(pkt, sub, 0))
+            return 0;
+    }
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_finish_int(WPACKET *pkt)
+{
+    int ret;
+
+    /*
+     * Internal API, so should not fail - but we do negative testing of this
+     * so no assert (otherwise the tests fail)
+     */
+    if (pkt->subs == NULL || pkt->subs->parent != NULL)
+        return 0;
+
+    ret = wpacket_intern_close(pkt, pkt->subs, 1);
+    if (ret) {
+        wpacket_sub_free(pkt, pkt->subs);
+        pkt->subs = NULL;
+    }
+
+    return ret;
+}
+
+static ossl_inline ossl_unused int WPACKET_close_int(WPACKET *pkt)
+{
+    /*
+     * Internal API, so should not fail - but we do negative testing of this
+     * so no assert (otherwise the tests fail)
+     */
+    if (pkt->subs == NULL || pkt->subs->parent == NULL)
+        return 0;
+
+    return wpacket_intern_close(pkt, pkt->subs, 1);
+}
+
+static ossl_inline ossl_unused int WPACKET_memcpy_int(WPACKET *pkt, const void *src, size_t len)
+{
+    unsigned char *dest;
+
+    if (len == 0)
+        return 1;
+
+    if (!WPACKET_allocate_bytes_int(pkt, len, &dest))
+        return 0;
+
+    if (dest != NULL)
+        memcpy(dest, src, len);
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_sub_allocate_bytes___int(WPACKET *pkt, size_t len,
+    unsigned char **allocbytes, size_t lenbytes)
+{
+    if (!WPACKET_start_sub_packet_len___int(pkt, lenbytes)
+        || !WPACKET_allocate_bytes_int(pkt, len, allocbytes)
+        || !WPACKET_close_int(pkt))
+        return 0;
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_sub_memcpy___int(WPACKET *pkt, const void *src, size_t len,
+    size_t lenbytes)
+{
+    if (!WPACKET_start_sub_packet_len___int(pkt, lenbytes)
+        || !WPACKET_memcpy_int(pkt, src, len)
+        || !WPACKET_close_int(pkt))
+        return 0;
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_is_null_buf_int(WPACKET *pkt)
+{
+    return pkt->buf == NULL && pkt->staticbuf == NULL;
+}
+
+static ossl_inline ossl_unused int WPACKET_get_length_int(WPACKET *pkt, size_t *len)
+{
+    /* Internal API, so should not fail */
+    if (!ossl_assert(pkt->subs != NULL && len != NULL))
+        return 0;
+
+    *len = pkt->written - pkt->subs->pwritten;
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_set_flags_int(WPACKET *pkt, unsigned int flags)
+{
+    /* Internal API, so should not fail */
+    if (!ossl_assert(pkt->subs != NULL))
+        return 0;
+
+    /*
+     * Preserve the inline flag to prevent freeing
+     */
+    pkt->subs->flags = (pkt->subs->flags & WPACKET_FLAGS_SUB_INLINE) | flags;
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_get_total_written_int(WPACKET *pkt, size_t *written)
+{
+    /* Internal API, so should not fail */
+    if (!ossl_assert(written != NULL))
+        return 0;
+
+    *written = pkt->written;
+
+    return 1;
+}
+
+static ossl_inline ossl_unused int WPACKET_set_max_size_int(WPACKET *pkt, size_t maxsize)
+{
+    WPACKET_SUB *sub;
+    size_t lenbytes;
+
+    /* Internal API, so should not fail */
+    if (!ossl_assert(pkt->subs != NULL))
+        return 0;
+
+    /* Find the WPACKET_SUB for the top level */
+    for (sub = pkt->subs; sub->parent != NULL; sub = sub->parent)
+        continue;
+
+    lenbytes = sub->lenbytes;
+    if (lenbytes == 0)
+        lenbytes = sizeof(pkt->maxsize);
+
+    if (maxmaxsize(lenbytes) < maxsize || maxsize < pkt->written)
+        return 0;
+
+    pkt->maxsize = maxsize;
+
+    return 1;
+}
+
+static ossl_inline ossl_unused void WPACKET_cleanup_int(WPACKET *pkt)
+{
+    WPACKET_SUB *sub, *parent;
+
+    for (sub = pkt->subs; sub != NULL; sub = parent) {
+        parent = sub->parent;
+        wpacket_sub_free(pkt, sub);
+    }
+    pkt->subs = NULL;
+}
+
+#endif /* OSSL_INTERNAL_PACKET_INLINE_H */
diff --git a/test/wpackettest.c b/test/wpackettest.c
index 05de73f002..b33f0e3bf9 100644
--- a/test/wpackettest.c
+++ b/test/wpackettest.c
@@ -189,7 +189,7 @@ static int test_WPACKET_start_sub_packet(void)
 {
     WPACKET pkt;
     size_t written;
-    size_t len;
+    size_t len = 0;

     if (!TEST_true(WPACKET_init(&pkt, buf))
         || !TEST_true(WPACKET_start_sub_packet(&pkt))
@@ -330,7 +330,7 @@ static int test_WPACKET_allocate_bytes(void)
 {
     WPACKET pkt;
     size_t written;
-    unsigned char *bytes;
+    unsigned char *bytes = NULL;

     if (!TEST_true(WPACKET_init_len(&pkt, buf, 1))
         || !TEST_true(WPACKET_allocate_bytes(&pkt, 2, &bytes)))
@@ -456,7 +456,7 @@ static int test_WPACKET_init_der(void)
 static int test_WPACKET_quic(void)
 {
     WPACKET pkt;
-    size_t written, len;
+    size_t written, len = 0;
     unsigned char *bytes;

     /* QUIC sub-packet with 4-byte length prefix, containing a 1-byte vlint */