rte_ring_elem.h(3)

okay so bestie, welcome to rte_ring_elem.h(3) πŸ’– that's the Library Functions Manual, section 3, the C-library-functions girlies ✨ and this page is LONG, so we're doing it in three parts β€” this is part one. grab a snack. πŸ“


πŸ’– NAME πŸ’–

rte_ring_elem.h

that's it. that's the name. she's a header, she contains multitudes, and the NAME section is just her name. no cap πŸ’…


✨ SYNOPSIS ✨

okay so before you can even look at her, you have to invite the whole friend group in. all of them. no "oh I'll just grab the one I need" πŸ™ˆ the synopsis is literally:

#include <rte_ring_core.h>
#include <rte_ring_elem_pvt.h>
#include <rte_ring_hts.h>
#include <rte_ring_rts.h>
#include <rte_ring_peek.h>
#include <rte_ring_peek_zc.h>
#include <rte_ring.h>

🌸 Functions 🌸

and here's the whole cast, fr fr β€” every single one of these shows up in the group chat:

ssize_t rte_ring_get_memsize_elem (unsigned int esize, unsigned int count)
struct rte_ring * rte_ring_create_elem (const char *name, unsigned int esize, unsigned int count, int socket_id, unsigned int flags)
static __rte_always_inline unsigned int rte_ring_mp_enqueue_bulk_elem (struct rte_ring *r, const void *obj_table, unsigned int esize, unsigned int n, unsigned int *free_space)
static __rte_always_inline unsigned int rte_ring_sp_enqueue_bulk_elem (struct rte_ring *r, const void *obj_table, unsigned int esize, unsigned int n, unsigned int *free_space)
static __rte_always_inline unsigned int rte_ring_enqueue_bulk_elem (struct rte_ring *r, const void *obj_table, unsigned int esize, unsigned int n, unsigned int *free_space)
static __rte_always_inline int rte_ring_mp_enqueue_elem (struct rte_ring *r, void *obj, unsigned int esize)
static __rte_always_inline int rte_ring_sp_enqueue_elem (struct rte_ring *r, void *obj, unsigned int esize)
static __rte_always_inline int rte_ring_enqueue_elem (struct rte_ring *r, void *obj, unsigned int esize)
static __rte_always_inline unsigned int rte_ring_mc_dequeue_bulk_elem (struct rte_ring *r, void *obj_table, unsigned int esize, unsigned int n, unsigned int *available)
static __rte_always_inline unsigned int rte_ring_sc_dequeue_bulk_elem (struct rte_ring *r, void *obj_table, unsigned int esize, unsigned int n, unsigned int *available)
static __rte_always_inline unsigned int rte_ring_dequeue_bulk_elem (struct rte_ring *r, void *obj_table, unsigned int esize, unsigned int n, unsigned int *available)
static __rte_always_inline int rte_ring_mc_dequeue_elem (struct rte_ring *r, void *obj_p, unsigned int esize)
static __rte_always_inline int rte_ring_sc_dequeue_elem (struct rte_ring *r, void *obj_p, unsigned int esize)
static __rte_always_inline int rte_ring_dequeue_elem (struct rte_ring *r, void *obj_p, unsigned int esize)
static __rte_always_inline unsigned int rte_ring_mp_enqueue_burst_elem (struct rte_ring *r, const void *obj_table, unsigned int esize, unsigned int n, unsigned int *free_space)
static __rte_always_inline unsigned int rte_ring_sp_enqueue_burst_elem (struct rte_ring *r, const void *obj_table, unsigned int esize, unsigned int n, unsigned int *free_space)
static __rte_always_inline unsigned int rte_ring_enqueue_burst_elem (struct rte_ring *r, const void *obj_table, unsigned int esize, unsigned int n, unsigned int *free_space)
static __rte_always_inline unsigned int rte_ring_mc_dequeue_burst_elem (struct rte_ring *r, void *obj_table, unsigned int esize, unsigned int n, unsigned int *available)
static __rte_always_inline unsigned int rte_ring_sc_dequeue_burst_elem (struct rte_ring *r, void *obj_table, unsigned int esize, unsigned int n, unsigned int *available)
static __rte_always_inline unsigned int rte_ring_dequeue_burst_elem (struct rte_ring *r, void *obj_table, unsigned int esize, unsigned int n, unsigned int *available)

see the pattern? πŸ‘€ there's the _mp_ / _sp_ / _mc_ / _sc_ girlies, and then the plain-named ones that just do whatever the ring was configured to do. the plain ones are the "read the room" versions. we love her.


🌈 Detailed Description 🌈

she is an RTE Ring with user defined element size πŸ’… that's the whole vibe: a ring where you pick how big each element is, instead of the ring being locked to some fixed pointer size.

Definition in file rte_ring_elem.h.


πŸ‘‘ Function Documentation πŸ‘‘

πŸ’« ssize_t rte_ring_get_memsize_elem (unsigned int esize, unsigned int count)

she calculates the memory size needed for a ring β€” with the element size you asked for. ✨

so like, this function gives you back the number of bytes needed for a ring, given the number of elements in it and the size of the element. that value is the sum of the size of the structure rte_ring and the size of the memory needed for storing the elements, and the whole thing is aligned to a cache line size. obviously. she's not gonna hand you some unaligned mess 😀

Parameters

  • esize β€” the size of ring element, in bytes. it must be a multiple of 4.
  • count β€” the number of elements in the ring (must be a power of 2).

Returns

  • the memory size needed for the ring on success πŸ’–
  • -EINVAL β€” esize is not a multiple of 4, or count provided is not a power of 2 😭

πŸ’« struct rte_ring * rte_ring_create_elem (const char * name, unsigned int esize, unsigned int count, int socket_id, unsigned int flags)

she creates a brand new ring named name that stores elements with the given size. main character behavior, honestly πŸ‘‘

here's the tea on how she does it: this function uses memzone_reserve() to allocate memory, and then it calls rte_ring_init() to initialize an empty ring. two-step process, very demure, very mindful.

the new ring size is set to count, which must be a power of two. water marking is disabled by default. and this part is important so I'm saying it loud: the real usable ring size is count-1 instead of count β€” that's how she tells a full ring apart from an empty ring. she needs that one extra slot to know the difference. iconic problem-solving, no notes.

the ring is added in RTE_TAILQ_RING list.

Parameters

  • name β€” the name of the ring.
  • esize β€” the size of ring element, in bytes. it must be a multiple of 4.
  • count β€” the number of elements in the ring (must be a power of 2).
  • socket_id β€” the socket_id argument is the socket identifier in case of NUMA. the value can be SOCKET_ID_ANY if there is no NUMA constraint for the reserved zone.
  • flags β€” an OR of the following:
    • one of mutually exclusive flags that define producer behavior:
      • RING_F_SP_ENQ: if this flag is set, the default behavior when using rte_ring_enqueue() or rte_ring_enqueue_bulk() is "single-producer".
      • RING_F_MP_RTS_ENQ: if this flag is set, the default behavior when using rte_ring_enqueue() or rte_ring_enqueue_bulk() is "multi-producer RTS mode".
      • RING_F_MP_HTS_ENQ: if this flag is set, the default behavior when using rte_ring_enqueue() or rte_ring_enqueue_bulk() is "multi-producer HTS mode". if none of these flags is set, then default "multi-producer" behavior is selected.
    • one of mutually exclusive flags that define consumer behavior:
      • RING_F_SC_DEQ: if this flag is set, the default behavior when using rte_ring_dequeue() or rte_ring_dequeue_bulk() is "single-consumer". otherwise, it is "multi-consumers".
      • RING_F_MC_RTS_DEQ: if this flag is set, the default behavior when using rte_ring_dequeue() or rte_ring_dequeue_bulk() is "multi-consumer RTS mode".
      • RING_F_MC_HTS_DEQ: if this flag is set, the default behavior when using rte_ring_dequeue() or rte_ring_dequeue_bulk() is "multi-consumer HTS mode". if none of these flags is set, then default "multi-consumer" behavior is selected.

Returns

on success, the pointer to the new allocated ring. NULL on error, with rte_errno set appropriately πŸ’€ possible errno values include:

  • E_RTE_NO_CONFIG β€” function could not get pointer to rte_config structure
  • EINVAL β€” esize is not a multiple of 4, or count provided is not a power of 2.
  • ENOSPC β€” the maximum number of memzones has already been allocated
  • EEXIST β€” a memzone with the same name already exists
  • ENOMEM β€” no appropriate memory area found in which to create memzone

πŸ’« __rte_always_inline unsigned int rte_ring_mp_enqueue_bulk_elem (struct rte_ring * r, const void * obj_table, unsigned int esize, unsigned int n, unsigned int * free_space) [static]

enqueue several objects on the ring β€” and she's multi-producers safe, bestie πŸ”₯

this function uses a "compare and set" instruction to move the producer index atomically. periodt.

Parameters

  • r β€” a pointer to the ring structure.
  • obj_table β€” a pointer to a table of objects.
  • esize β€” the size of ring element, in bytes. it must be a multiple of 4. this must be the same value used while creating the ring. otherwise the results are undefined. ⚠️ like, actually undefined. don't play with her.
  • n β€” the number of objects to add in the ring from the obj_table.
  • free_space β€” if non-NULL, returns the amount of space in the ring after the enqueue operation has finished.

Returns

the number of objects enqueued, either 0 or n

Definition at line 127 of file rte_ring_elem.h.


πŸ’« __rte_always_inline unsigned int rte_ring_sp_enqueue_bulk_elem (struct rte_ring * r, const void * obj_table, unsigned int esize, unsigned int n, unsigned int * free_space) [static]

enqueue several objects on a ring ✨

⚠️ Warning: this API is NOT multi-producers safe. like, at all. if you bring a crowd to this function she will simply not be okay. single producer only, pls and thank you πŸ™

Parameters

  • r β€” a pointer to the ring structure.
  • obj_table β€” a pointer to a table of objects.
  • esize β€” the size of ring element, in bytes. it must be a multiple of 4. this must be the same value used while creating the ring. otherwise the results are undefined.
  • n β€” the number of objects to add in the ring from the obj_table.
  • free_space β€” if non-NULL, returns the amount of space in the ring after the enqueue operation has finished.

Returns

the number of objects enqueued, either 0 or n

Definition at line 156 of file rte_ring_elem.h.


πŸ’« __rte_always_inline unsigned int rte_ring_enqueue_bulk_elem (struct rte_ring * r, const void * obj_table, unsigned int esize, unsigned int n, unsigned int * free_space) [static]

enqueue several objects on a ring. full stop, no qualifiers πŸ’…

this one calls the multi-producer or the single-producer version depending on the default behavior that was specified at ring creation time β€” you know, the flags from earlier πŸ‘€ so she's just doing what she was told. she's a good listener.

Parameters

  • r β€” a pointer to the ring structure.
  • obj_table β€” a pointer to a table of objects.
  • esize β€” the size of ring element, in bytes. it must be a multiple of 4. this must be the same value used while creating the ring. otherwise the results are undefined.
  • n β€” the number of objects to add in the ring from the obj_table.
  • free_space β€” if non-NULL, returns the amount of space in the ring after the enqueue operation has finished.

Returns

the number of objects enqueued, either 0 or n

Definition at line 190 of file rte_ring_elem.h.


πŸ’« __rte_always_inline int rte_ring_mp_enqueue_elem (struct rte_ring * r, void * obj, unsigned int esize) [static]

enqueue one object on a ring β€” singular. one. just the one object. and she's multi-producers safe 🫢

this function uses a "compare and set" instruction to move the producer index atomically. same energy as her bulk sister, just for one lil guy.

Parameters

  • r β€” a pointer to the ring structure.
  • obj β€” a pointer to the object to be added.
  • esize β€” the size of ring element, in bytes. it must be a multiple of 4. this must be the same value used while creating the ring. otherwise the results are undefined.

Returns

  • 0: Success; objects enqueued. πŸ’–
  • -ENOBUFS: Not enough room in the ring to enqueue; no object is enqueued. 😭 nothing went in. she checked, there was no room, she said nope.

Definition at line 234 of file rte_ring_elem.h.


and that's where part one lets out, bestie β€” because we have SO many more girlies to meet in the enqueue-burst, dequeue, and dequeue-burst lineups ✨

rte_ring_sp_enqueue_elem</p>

__rte_always_inline int rte_ring_sp_enqueue_elem (struct rte_ring * r, void * obj, unsigned int esize) [static]

Okay bestie, this one drops one object onto a ring. One. A single lil guy. πŸ’…

⚠️ Warning: this API is NOT multi-producers safe β€” like, do NOT let multiple threads call this at the same time, it is not the vibe. 🚫

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj β€” a pointer to the object you're adding.
  • esize β€” the size of the ring element, in bytes. It must be a multiple of 4. And fr fr it must be the same value you used while creating the ring β€” otherwise the results are undefined. 😭

Returns ✨

  • 0: success, objects enqueued.
  • -ENOBUFS: not enough room in the ring to enqueue β€” no object gets enqueued.

Definition at line 258 of file rte_ring_elem.h.


rte_ring_enqueue_elem</p>

__rte_always_inline int rte_ring_enqueue_elem (struct rte_ring * r, void * obj, unsigned int esize) [static]

Enqueue one object on a ring. 🫢

This one is the ✨diplomatic✨ version β€” it calls the multi-producer or the single-producer version, depending on the default behaviour you specified back at ring creation time (see flags). It just knows what to do, no cap.

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj β€” a pointer to the object you're adding.
  • esize β€” the size of the ring element, in bytes. Must be a multiple of 4, and must match the value used while creating the ring β€” otherwise undefined results. πŸ™ˆ

Returns ✨

  • 0: success, objects enqueued.
  • -ENOBUFS: not enough room in the ring to enqueue; no object is enqueued.

Definition at line 284 of file rte_ring_elem.h.


rte_ring_mc_dequeue_bulk_elem</p>

__rte_always_inline unsigned int rte_ring_mc_dequeue_bulk_elem (struct rte_ring * r, void * obj_table, unsigned int esize, unsigned int n, unsigned int * available) [static]

Dequeue several objects from a ring β€” and this one is multi-consumers safe. πŸ‘‘

It uses a "compare and set" instruction to move the consumer index atomically. Iconic behaviour tbh.

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj_table β€” a pointer to a table of objects that will be filled.
  • esize β€” the size of the ring element, in bytes. Multiple of 4 please, and same value as ring creation, else undefined.
  • n β€” the number of objects to dequeue from the ring to the obj_table.
  • available β€” if non-NULL, returns the number of remaining ring entries after the dequeue has finished.

Returns ✨

The number of objects dequeued β€” either 0 or n.

Definition at line 313 of file rte_ring_elem.h.


rte_ring_sc_dequeue_bulk_elem</p>

__rte_always_inline unsigned int rte_ring_sc_dequeue_bulk_elem (struct rte_ring * r, void * obj_table, unsigned int esize, unsigned int n, unsigned int * available) [static]

Dequeue several objects from a ring β€” NOT multi-consumers safe. πŸ’€ One consumer only, bestie, don't push it.

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj_table β€” a pointer to a table of objects that will be filled.
  • esize β€” the size of the ring element, in bytes. Multiple of 4, same value as creation, else undefined.
  • n β€” the number of objects to dequeue from the ring to the obj_table; must be strictly positive.
  • available β€” if non-NULL, returns the number of remaining ring entries after the dequeue has finished.

Returns ✨

The number of objects dequeued β€” either 0 or n.

Definition at line 341 of file rte_ring_elem.h.


rte_ring_dequeue_bulk_elem</p>

__rte_always_inline unsigned int rte_ring_dequeue_bulk_elem (struct rte_ring * r, void * obj_table, unsigned int esize, unsigned int n, unsigned int * available) [static]

Dequeue several objects from a ring. 🌸

Again, the ✨vibe-based✨ dispatcher β€” it calls the multi-consumers or the single-consumer version, depending on the default behaviour specified at ring creation time (see flags).

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj_table β€” a pointer to a table of objects that will be filled.
  • esize β€” the size of the ring element, in bytes. Multiple of 4, same as creation, else undefined.
  • n β€” the number of objects to dequeue from the ring to the obj_table.
  • available β€” if non-NULL, returns the number of remaining ring entries after the dequeue has finished.

Returns ✨

The number of objects dequeued β€” either 0 or n.

Definition at line 372 of file rte_ring_elem.h.


rte_ring_mc_dequeue_elem</p>

__rte_always_inline int rte_ring_mc_dequeue_elem (struct rte_ring * r, void * obj_p, unsigned int esize) [static]

Dequeue one object from a ring β€” multi-consumers safe. πŸ’–

Uses a "compare and set" instruction to move the consumer index atomically. So smooth.

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj_p β€” a pointer to the object that will be filled.
  • esize β€” the size of the ring element, in bytes. Multiple of 4, same as creation, else undefined.

Returns ✨

  • 0: success, objects dequeued.
  • -ENOENT: not enough entries in the ring to dequeue; no object is dequeued.

Definition at line 417 of file rte_ring_elem.h.


rte_ring_sc_dequeue_elem</p>

__rte_always_inline int rte_ring_sc_dequeue_elem (struct rte_ring * r, void * obj_p, unsigned int esize) [static]

Dequeue one object from a ring β€” NOT multi-consumers safe. πŸ™ˆ

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj_p β€” a pointer to the object that will be filled.
  • esize β€” the size of the ring element, in bytes. Multiple of 4, same as creation, else undefined.

Returns ✨

  • 0: success, objects dequeued.
  • -ENOENT: not enough entries in the ring to dequeue, no object is dequeued.

Definition at line 441 of file rte_ring_elem.h.


rte_ring_dequeue_elem</p>

__rte_always_inline int rte_ring_dequeue_elem (struct rte_ring * r, void * obj_p, unsigned int esize) [static]

Dequeue one object from a ring. πŸ¦‹

You already know the drill β€” it calls the multi-consumers or the single-consumer version depending on the default behaviour specified at ring creation time (see flags).

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj_p β€” a pointer to the object that will be filled.
  • esize β€” the size of the ring element, in bytes. Multiple of 4, same as creation, else undefined.

Returns ✨

  • 0: success, objects dequeued.
  • -ENOENT: not enough entries in the ring to dequeue, no object is dequeued.

Definition at line 469 of file rte_ring_elem.h.


rte_ring_mp_enqueue_burst_elem</p>

__rte_always_inline unsigned int rte_ring_mp_enqueue_burst_elem (struct rte_ring * r, const void * obj_table, unsigned int esize, unsigned int n, unsigned int * free_space) [static]

Enqueue several objects on the ring β€” multi-producers safe. πŸ”₯

It uses a "compare and set" instruction to move the producer index atomically. So it can handle the chaos. ✨

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj_table β€” a pointer to a table of objects.
  • esize β€” the size of the ring element, in bytes. Multiple of 4, same as creation, else undefined.
  • n β€” the number of objects to add in the ring from the obj_table.
  • free_space β€” if non-NULL, returns the amount of space in the ring after the enqueue operation has finished.

Returns ✨

  • n: actual number of objects enqueued.

Definition at line 498 of file rte_ring_elem.h.


rte_ring_sp_enqueue_burst_elem</p>

__rte_always_inline unsigned int rte_ring_sp_enqueue_burst_elem (struct rte_ring * r, const void * obj_table, unsigned int esize, unsigned int n, unsigned int * free_space) [static]

Enqueue several objects on a ring.

⚠️ Warning: this API is NOT multi-producers safe. Keep it single, keep it safe. πŸ’…

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj_table β€” a pointer to a table of objects.
  • esize β€” the size of the ring element, in bytes. Multiple of 4, same as creation, else undefined.
  • n β€” the number of objects to add in the ring from the obj_table.
  • free_space β€” if non-NULL, returns the amount of space in the ring after the enqueue operation has finished.

Returns ✨

  • n: actual number of objects enqueued.

Definition at line 527 of file rte_ring_elem.h.


rte_ring_enqueue_burst_elem</p>

__rte_always_inline unsigned int rte_ring_enqueue_burst_elem (struct rte_ring * r, const void * obj_table, unsigned int esize, unsigned int n, unsigned int * free_space) [static]

Enqueue several objects on a ring. 🌈

And yes, it's the flex-y dispatcher one more time β€” it calls the multi-producer or the single-producer version depending on the default behavior that was specified at ring creation time (see flags).

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj_table β€” a pointer to a table of objects.
  • esize β€” the size of the ring element, in bytes. Multiple of 4, same as creation, else undefined.
  • n β€” the number of objects to add in the ring from the obj_table.
  • free_space β€” if non-NULL, returns the amount of space in the ring after the enqueue operation has finished.

Returns ✨

  • n: actual number of objects enqueued.

Definition at line 558 of file rte_ring_elem.h.


rte_ring_mc_dequeue_burst_elem</p>

__rte_always_inline unsigned int rte_ring_mc_dequeue_burst_elem (struct rte_ring * r, void * obj_table, unsigned int esize, unsigned int n, unsigned int * available) [static]

Dequeue several objects from a ring β€” multi-consumers safe. And here's the tea: when the requested objects are more than the available objects, it only dequeues the actual number of objects. πŸ“

It uses a "compare and set" instruction to move the consumer index atomically. Obsessed.

Parameters πŸŽ€

  • r β€” a pointer to the ring structure.
  • obj_table β€” a pointer to a table of objects that will be filled.
  • esize β€” the size of the ring element, in bytes. Multiple of 4, same as creation, else undefined.
  • n β€” the number of objects to dequeue from the ring to the obj_table.
  • available β€” if non-NULL, returns the number of remaining ring entries after the dequeue has finished.

Returns ✨

  • n: actual number of objects dequeued, 0 if ring is empty.

Definition at line 608 of file rte_ring_elem.h.


rte_ring_sc_dequeue_burst_elem</p>

__rte_always_inline unsigned int rte_ring_sc_dequeue_burst_elem (struct rte_ring * r, void * obj_table, unsigned int esize, unsigned int n, unsigned int * available) [static]

Dequeue several objects from a ring β€” NOT multi-consumers safe. When the requested objects are more than the available objects, only dequeue the actual number of objects. 🧚

bestie the params for that one we were just talking about are giving:

  • r β€” a pointer to the ring structure. like, the actual ring, okay? πŸ’
  • obj_table β€” a pointer to a table of objects that will be filled. it's giving empty and ready to be filled πŸ₯€
  • esize β€” the size of ring element, in bytes. it must be a multiple of 4. this must be the same value used while creating the ring. otherwise the results are undefined β€” and we don't do undefined, that's chaotic 😭
  • n β€” the number of objects to dequeue from the ring to the obj_table.
  • available β€” if non-NULL, returns the number of remaining ring entries after the dequeue has finished. lowkey a little status update for you πŸ‘€

Returns? slay:

  • n: Actual number of objects dequeued, 0 if ring is empty.

Definition at line 637 of file rte_ring_elem.h.


okay next up, this icon:

   __rte_always_inline unsigned int rte_ring_dequeue_burst_elem (struct
       rte_ring * r, void * obj_table, unsigned int esize, unsigned int n,
       unsigned int * available) [static]

Dequeue multiple objects from a ring up to a maximum number. periodt.

This function calls the multi-consumers or the single-consumer version, depending on the default behaviour that was specified at ring creation time (see flags). it's giving "choose your fighter" energy πŸ₯Š

Parameters (yes again, but they're the same bestie):

  • r β€” a pointer to the ring structure. the ring, the myth, the legend πŸ’«
  • obj_table β€” a pointer to a table of objects that will be filled.
  • esize β€” the size of ring element, in bytes. must be a multiple of 4. must be the same value used while creating the ring. otherwise undefined results, no cap 🚫
  • n β€” the number of objects to dequeue from the ring to the obj_table.
  • available β€” if non-NULL, returns the number of remaining ring entries after the dequeue has finished.

Returns:

  • Number of objects dequeued

Definition at line 668 of file rte_ring_elem.h.


πŸ‘‘ Author

Generated automatically by Doxygen for DPDK from the source code. yep, the bots wrote it, but we made it cute πŸ’…


that's the whole vibe bestie. this page is from DPDK version 25.11.3, the one and only rte_ring_elem.h(3). hope you ate and left no crumbs πŸŽ€βœ¨

πŸ“¦ where this page comes from