<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ilean Monterrubio Jr</title>
    <description>The latest articles on DEV Community by Ilean Monterrubio Jr (@elcapitan).</description>
    <link>https://dev.to/elcapitan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2648828%2F4d9e8133-29de-4164-b93c-8376ddd05528.png</url>
      <title>DEV Community: Ilean Monterrubio Jr</title>
      <link>https://dev.to/elcapitan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/elcapitan"/>
    <language>en</language>
    <item>
      <title>Patrones embebidos comunes: pool de memoria</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Thu, 10 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/patrones-embebidos-comunes-pool-de-memoria-1l8</link>
      <guid>https://dev.to/elcapitan/patrones-embebidos-comunes-pool-de-memoria-1l8</guid>
      <description>&lt;p&gt;Esta es la segunda parte de la serie donde vuelvo a explorar los patrones embebidos más comunes. Esta vez le toca al pool de memoria (memory pool). Este patrón se usa a menudo como alternativa a &lt;code&gt;malloc()&lt;/code&gt; y &lt;code&gt;free()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;El código fuente completo y probado vive en el repositorio complementario: &lt;a href="https://github.com/ileanmjr88/tetzontli" rel="noopener noreferrer"&gt;ileanmjr88/tetzontli&lt;/a&gt;. Cada patrón de esta serie es su propio módulo con una suite completa de pruebas en GoogleTest, así que puedes clonarlo y ejecutar las pruebas tú mismo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concepto básico
&lt;/h2&gt;

&lt;p&gt;Empecemos con una comparación rápida contra el búfer circular. Ambos patrones sirven para guardar datos, pero difieren en el punto de vista desde el cual lo hacen. Al búfer circular lo llamamos contenedor. Cada elemento que se inserta en la estructura de datos es homogéneo y uniforme, y el búfer es dueño del orden de esos datos: se empuja por un lado y se saca por el otro, siguiendo FIFO. El búfer sabe qué hay dentro, al menos en cuanto a tamaño y secuencia.&lt;/p&gt;

&lt;p&gt;El pool de memoria toma el punto de vista opuesto. No es un contenedor, es gestión de memoria propia. No conoce la estructura de los datos que se guardan, no le importa qué contienen, y no lleva registro de la secuencia. Uno pide un bloque, el pool revisa si hay alguno disponible, y si lo hay, devuelve un puntero a ese bloque. Desde el punto de vista del pool solo existen bloques ocupados o libres, sin referencia a su estructura ni al orden en que se entregaron.&lt;/p&gt;

&lt;p&gt;Llegados a este punto, la pregunta obvia es qué es un pool de memoria en los términos más simples. Es un búfer de bloques de memoria del mismo tamaño, normalmente implementado como un arreglo, así que tenemos un trozo o pool de memoria del cual tomar. Nuestro &lt;code&gt;init&lt;/code&gt; determina el tamaño de los bloques e inicializa la estructura administradora que lleva registro de dónde está el siguiente bloque disponible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Un repaso rápido a &lt;code&gt;malloc()&lt;/code&gt; y &lt;code&gt;free()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Antes de entrar al pool de memoria en sí, vale la pena dar un paso atrás y recordar qué hacen realmente &lt;code&gt;malloc()&lt;/code&gt; y &lt;code&gt;free()&lt;/code&gt;. Es importante porque vamos a implementar una funcionalidad parecida. La diferencia es que nosotros reservamos por adelantado un búfer de tamaño fijo, normalmente un arreglo estático, mientras que estas funciones reservan y liberan memoria del heap de forma dinámica.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;malloc()&lt;/code&gt; le pide al asignador del heap un trozo de memoria del tamaño del elemento que se quiere guardar. Si la reserva tiene éxito, es decir, si el asignador encontró espacio, devuelve un puntero. Si no logra encontrar espacio devuelve &lt;code&gt;NULL&lt;/code&gt;, indicando el fallo. El asignador del heap gestiona esto llevando su propio registro interno de los trozos libres y usados. Cada vez que usamos &lt;code&gt;malloc()&lt;/code&gt; lleva registro de cuánto usamos, y lo mismo con &lt;code&gt;free()&lt;/code&gt;: cada vez que liberamos memoria la marca como disponible. &lt;code&gt;free()&lt;/code&gt; hace lo contrario de &lt;code&gt;malloc()&lt;/code&gt;: se llama con el puntero que queremos devolverle al asignador del heap, este marca ese trozo como libre, y sigue adelante.&lt;/p&gt;

&lt;p&gt;Estas dos funciones para reservar y liberar memoria dinámicamente funcionan muy bien en computadoras modernas y en granjas de servidores, donde hay grandes cantidades de memoria y un espacio de direcciones amplio. Si un &lt;code&gt;malloc()&lt;/code&gt; tarda un microsegundo más en encontrar un trozo de memoria no pasa nada. Los sistemas embebidos no pueden hacer esas mismas suposiciones, y ahí es donde empiezan a aparecer las grietas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fragmentación&lt;/strong&gt; : a medida que reservamos y liberamos trozos de tamaños mezclados, la memoria libre que va quedando deja de ser contigua. Con el tiempo una petición falla aunque en total haya bytes libres de sobra, solo que no hay suficientes juntos. El fallo llega tarde, es intermitente y no se reproduce en el banco de pruebas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiempos no deterministas&lt;/strong&gt; : no todo sistema embebido necesita tiempos deterministas, pero para los que sí, &lt;code&gt;malloc()&lt;/code&gt; es un problema. El asignador busca un trozo que quepa, así que cuánto tarda depende del estado del heap. Eso es difícil de aceptar en un bucle con plazos estrictos, e imposible de presupuestar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallo tardío&lt;/strong&gt; : uno se entera de que se quedó sin memoria justo en el momento en que necesita memoria, que suele ser el momento en que menos puede hacer al respecto. No hay respuesta en tiempo de arranque ni en tiempo de enlazado a la pregunta «¿esto va a caber?».&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Llevar el registro de los bloques disponibles
&lt;/h2&gt;

&lt;p&gt;Que no usemos &lt;code&gt;malloc()&lt;/code&gt; ni &lt;code&gt;free()&lt;/code&gt; también significa que no usamos el administrador del heap, que llevaba su propio registro de qué trozos estaban libres y cuáles entregados. Nuestra implementación tiene que hacer esa contabilidad por su cuenta. En realidad solo hay dos cosas que necesita saber: si hay algún bloque disponible, y si lo hay, dónde está.&lt;/p&gt;

&lt;p&gt;Antes de entrar en la implementación que se usa en el repositorio tetzontli, exploremos brevemente dos enfoques comunes para esta contabilidad. El primero es el mapa de bits (bitmap), donde cada bit de un entero indica si un bloque está en uso o libre. La cantidad de bloques determina el tipo de entero que se usa. La alternativa es el método de la lista de libres (free list), que enhebra los bloques disponibles en una lista enlazada y mantiene un puntero apuntando al siguiente bloque disponible. Al liberar, el bloque vuelve al frente de ese puntero. Hay una ventaja clara en el método de la lista de libres: nunca tiene que buscar. Tomar un bloque significa quedarse con aquello a lo que apunta el puntero de seguimiento, y devolver uno significa ponerlo al frente. Ambas son una sola actualización de puntero, sin importar qué tan lleno esté el pool. El mapa de bits tiene que recorrer buscando un bit libre, y cuánto tarda depende de cuántos bloques ya estén en uso. Eso sí, el mapa de bits tiene una ventaja a la que estamos renunciando: como registra el estado de cada bloque, puede distinguir una doble liberación de una legítima. La lista de libres no puede, y volveremos a lo que eso nos cuesta.&lt;/p&gt;

&lt;p&gt;La implementación de tetzontli usa el enfoque de lista de libres intrusiva. Es una variación común donde cada bloque libre sabe por sí mismo dónde está el siguiente trozo de memoria disponible. La razón de este enfoque es que no hace falta un mapa de bits para llevar registro de los bloques disponibles, ya que nuestro puntero &lt;code&gt;next&lt;/code&gt; ya sabe cuál es el siguiente bloque libre. También un recordatorio: como no reservamos memoria dinámicamente, usamos un arreglo como contenedor para crear el pool de memoria. Eso facilita el recorrido después del &lt;code&gt;init&lt;/code&gt;. Una vez que empezamos a reservar y liberar, queremos que los bloques liberados más recientemente sean los primeros en entregarse, manteniéndolo como una pila, lo cual es más amigable con caché.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementación
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Estructura del pool de memoria
&lt;/h3&gt;

&lt;p&gt;Igual que en el búfer circular, vamos a crear un struct. Es una forma simple de mantener todos los parámetros juntos y accesibles desde un solo puntero.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.h&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;high_water&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;uint8_t *base&lt;/code&gt;: la dirección del bloque &lt;code&gt;0&lt;/code&gt;, y el ancla desde la cual se mide todo el pool. Cada bloque vive en &lt;code&gt;base + i * block_size&lt;/code&gt;. Es tentador leer esto como el primer byte del búfer que se le entrega al pool, y la mayoría de las veces eso es exactamente lo que es, pero los dos pueden separarse, por una razón que veremos en &lt;code&gt;memory_pool_init()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size_t block_size&lt;/code&gt;: esto es lo que solemos llamar el stride. Define el tamaño de cada bloque de memoria y qué tan separados están entre sí.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size_t capacity&lt;/code&gt;: la cantidad de bloques del pool. Se fija en &lt;code&gt;memory_pool_init()&lt;/code&gt; y nunca cambia. Cuántos están libres en un momento dado es &lt;code&gt;capacity - used&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size_t used&lt;/code&gt;: la cantidad de bloques entregados. &lt;code&gt;memory_pool_alloc()&lt;/code&gt; lo incrementa y &lt;code&gt;memory_pool_free()&lt;/code&gt; lo decrementa. Ojo: entregados, no en uso. El pool no tiene idea de qué se hace con un bloque, solo de si lo entregó y no lo ha recuperado.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size_t high_water&lt;/code&gt;: el valor máximo que &lt;code&gt;used&lt;/code&gt; ha alcanzado desde &lt;code&gt;memory_pool_init()&lt;/code&gt;. Este es el número con el que se dimensiona el pool: ejecutar el sistema bajo la peor carga, leerlo de vuelta, y así se sabe cuántos bloques hacían falta realmente. Sobrevive deliberadamente a &lt;code&gt;memory_pool_reset()&lt;/code&gt;. Devolver todos los bloques no cambia el hecho de que en algún momento se necesitaron todos esos a la vez, y borrar el pico tiraría a la basura la medición. Solo un &lt;code&gt;memory_pool_init()&lt;/code&gt; nuevo lo reinicia.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;void *free_head&lt;/code&gt;: la cabeza de la lista de libres. El primer bloque disponible para entregar, o &lt;code&gt;NULL&lt;/code&gt; cuando todos los bloques están fuera. La lista es intrusiva, es decir, sus enlaces viven dentro de los propios bloques libres en vez de en structs de nodo aparte. Cada bloque libre guarda un puntero al siguiente en sus primeros bytes. Eso es lo que hace que la lista de libres no cueste memoria extra, y es la razón por la que &lt;code&gt;memory_pool_init()&lt;/code&gt; rechaza un &lt;code&gt;block_size&lt;/code&gt; más chico que un puntero.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Función de redondeo hacia arriba
&lt;/h3&gt;

&lt;p&gt;También vamos a aprovechar &lt;code&gt;is_power_of_two()&lt;/code&gt; en este patrón, y además agregamos un método nuevo para redondear hacia arriba al siguiente múltiplo de &lt;code&gt;a&lt;/code&gt;. Cubrimos &lt;code&gt;is_power_of_two()&lt;/code&gt; en detalle en el post del búfer circular, así que no lo repetimos aquí. Rechaza el cero además de los números que no son potencia de dos, que es lo que impide que &lt;code&gt;block_align = 0&lt;/code&gt; llegue a &lt;code&gt;round_up()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;round_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1u&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1u&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;(x + (a - 1u))&lt;/code&gt;: toma la variable &lt;code&gt;x&lt;/code&gt;, el número que queremos redondear hacia arriba, y la variable &lt;code&gt;a&lt;/code&gt;, la alineación a la que queremos redondearlo. Sumar &lt;code&gt;a - 1&lt;/code&gt; primero es lo que empuja cualquier valor que no esté ya en un límite más allá del siguiente.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~(a - 1u)&lt;/code&gt;: toma el número por el que queremos alinear, le resta uno, y aplica un not a nivel de bits, lo cual nos da una máscara que limpia los bits bajos y deja intacto todo lo que está por encima.&lt;/li&gt;
&lt;li&gt;Tomar ambos lados y hacer el &lt;code&gt;&amp;amp;&lt;/code&gt; a nivel de bits baja el valor de vuelta a un límite. Por sí solo eso redondearía hacia abajo. Sumar &lt;code&gt;a - 1&lt;/code&gt; primero es lo que lo convierte en un redondeo hacia arriba. Esto puede confundir a primera vista.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;llamada: round_up(x = 12, a = 16);

- (x + (a - 1u)) = (12 + (16 - 1u)) = (12 + 15) = 27 = 0b00011011
- (a - 1u) = (16 - 1u) = 15

Siguiendo la evaluación del lado derecho del &amp;amp;
- ~(15) = ~(0b00001111) = 0b11110000

Ahora juntando ambos lados
- 0b00011011 &amp;amp; 0b11110000 = 0b00010000 = 16

Es decir, hay que redondear 12 hacia arriba hasta 16

Y cuando el valor ya está en un límite, nada se mueve:
- round_up(16, 16) = (16 + 15) &amp;amp; ~15
- 0b00011111 &amp;amp; 0b11110000 = 0b00010000 = 16

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En el búfer circular, el truco de la máscara solo funcionaba porque la capacidad era potencia de dos. Aquí aplica la misma regla, salvo que recae sobre &lt;code&gt;a&lt;/code&gt;, la alineación a la que redondeamos, y no sobre la capacidad. &lt;code&gt;a&lt;/code&gt; tiene que ser potencia de dos, y por eso &lt;code&gt;memory_pool_init()&lt;/code&gt; le aplica &lt;code&gt;is_power_of_two()&lt;/code&gt; antes de hacer cualquier redondeo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ejemplo cuando a no es potencia de dos.

- round_up(12, 12) = (12 + 11) &amp;amp; ~11 = 23 &amp;amp; ~11
- 0b00010111 &amp;amp; ~0b00001011 = 0b00010111 &amp;amp; 0b11110100 = 0b00010100 = 20

Debería dar 12, ya que 12 ya es múltiplo de 12 y un redondeo correcto
lo dejaría igual. Falla porque 11 es 0b00001011, no una serie limpia
de unos, así que la máscara limpia los bits equivocados.
Por eso aquí se usa la potencia de dos, para poder optimizar round_up.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Funciones de lectura y escritura del siguiente
&lt;/h3&gt;

&lt;p&gt;Este par de funciones son las que leen cuál es el siguiente bloque disponible y actualizan cuál pasa a serlo. Empecemos con &lt;code&gt;read_next()&lt;/code&gt;: para cada reserva vamos a necesitar leer dónde está el siguiente bloque a entregar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;read_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;slot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta función toma el puntero &lt;code&gt;block&lt;/code&gt;, que llega como &lt;code&gt;const void *&lt;/code&gt;, y lo castea a &lt;code&gt;void *const *&lt;/code&gt;. Ese cast se lee de derecha a izquierda: &lt;code&gt;slot&lt;/code&gt; es un puntero a un puntero constante a &lt;code&gt;void&lt;/code&gt;. El &lt;code&gt;const&lt;/code&gt; aplica al puntero guardado dentro del bloque, no a &lt;code&gt;slot&lt;/code&gt; en sí, y está ahí para que el cast no descarte silenciosamente el calificador que el parámetro ya traía. Creamos la variable &lt;code&gt;void *next&lt;/code&gt; como contenedor al cual copiar. Hacemos una copia de memoria de &lt;code&gt;sizeof next&lt;/code&gt; bytes desde el slot hacia &lt;code&gt;next&lt;/code&gt;. Así es como devolvemos un puntero al siguiente bloque de datos disponible.&lt;/p&gt;

&lt;p&gt;Vale la pena detenerse en el &lt;code&gt;memcpy&lt;/code&gt;, porque la versión obvia es una sola línea, &lt;code&gt;return *(void **)block;&lt;/code&gt;. El problema es que esos bytes viven dentro de un arreglo de &lt;code&gt;uint8_t&lt;/code&gt;, así que leerlos a través de un &lt;code&gt;void**&lt;/code&gt; significa acceder a un objeto mediante un puntero de otro tipo, que es justo lo que prohíben las reglas de aliasing. El compilador tiene permiso de asumir que no hicimos eso. &lt;code&gt;memcpy&lt;/code&gt; es la manera sancionada de decir «copia estos bytes tal cual sin afirmar nada sobre tipos», y no cuesta nada en cualquier nivel de optimización por encima de &lt;code&gt;-O0&lt;/code&gt;: todo compilador reconoce un &lt;code&gt;memcpy&lt;/code&gt; del tamaño de un puntero y emite una sola carga o escritura. En una compilación de depuración se queda como una llamada real, lo cual conviene saber pero no amerita cambiar el código.&lt;/p&gt;

&lt;p&gt;Sigue &lt;code&gt;write_next()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;write_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;slot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta función es el espejo de &lt;code&gt;read_next()&lt;/code&gt;. La diferencia es que recibimos &lt;code&gt;void *block&lt;/code&gt; y declaramos &lt;code&gt;slot&lt;/code&gt; como &lt;code&gt;void **&lt;/code&gt;, asignándole el cast de &lt;code&gt;block&lt;/code&gt;, lo cual le dice al compilador que trate esos primeros bytes como un lugar donde vive un puntero. Después copiamos &lt;code&gt;sizeof next&lt;/code&gt; bytes desde &lt;code&gt;&amp;amp;next&lt;/code&gt;, la dirección de nuestra variable local, hacia el slot. Lo que queda en el bloque es el valor del puntero mismo. Ninguna de estas dos funciones mira nunca &lt;code&gt;block_size&lt;/code&gt;, solo tocan los primeros &lt;code&gt;sizeof(void *)&lt;/code&gt; bytes, y por eso la lista de libres cuesta lo mismo sin importar qué tan grandes sean los bloques.&lt;/p&gt;

&lt;h3&gt;
  
  
  Función de reinicio (reset)
&lt;/h3&gt;

&lt;p&gt;Antes de llegar a la función &lt;code&gt;init&lt;/code&gt;, conviene hablar de cómo reiniciamos el pool de memoria. Esta hace el trabajo de reenlazar los bloques de memoria en orden secuencial, lo cual la vuelve práctica para reutilizarla dentro de &lt;code&gt;init&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;write_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Empezamos con una verificación rápida de que no le pasamos un puntero &lt;code&gt;NULL&lt;/code&gt; a la función. Sin ella, la siguiente línea, &lt;code&gt;mp-&amp;gt;free_head = NULL&lt;/code&gt;, dereferenciaría ese handle nulo. En una computadora de escritorio eso es un fallo de segmentación. En la mayoría de los microcontroladores no hay MMU que lo atrape, así que la dirección cero es memoria real, a menudo la tabla de vectores, y la escritura tiene éxito en silencio y corrompe algo. Si &lt;code&gt;mp&lt;/code&gt; es una dirección de memoria real, ponemos &lt;code&gt;mp-&amp;gt;free_head = NULL&lt;/code&gt; para saber desde qué estado partimos antes de asignar el puntero siguiente en cada bloque.&lt;/p&gt;

&lt;p&gt;Después iteramos con &lt;code&gt;for (size_t i = mp-&amp;gt;capacity; i-- &amp;gt; 0;)&lt;/code&gt;. El decremento vive dentro de la comparación, así que la verificación va primero y el decremento después, lo que significa que el cuerpo ve desde &lt;code&gt;capacity - 1&lt;/code&gt; hasta &lt;code&gt;0&lt;/code&gt;, y el bucle termina después de ejecutar el &lt;code&gt;0&lt;/code&gt;. La versión obvia, &lt;code&gt;for (size_t i = capacity - 1; i &amp;gt;= 0; i--)&lt;/code&gt;, nunca termina. &lt;code&gt;size_t&lt;/code&gt; es sin signo, así que &lt;code&gt;i &amp;gt;= 0&lt;/code&gt; siempre es verdadero, y decrementar &lt;code&gt;0&lt;/code&gt; da la vuelta hasta &lt;code&gt;SIZE_MAX&lt;/code&gt; en lugar de volverse negativo. Este es el mismo desbordamiento sin signo en el que nos apoyamos con el &lt;code&gt;count&lt;/code&gt; del búfer circular, solo que aquí trabaja en nuestra contra.&lt;/p&gt;

&lt;p&gt;La dirección importa por una segunda razón. Cada pasada escribe la cabeza actual dentro de un bloque y luego hace de ese bloque la nueva cabeza, así que recorrer el arreglo hacia atrás produce una lista que sale hacia adelante. Después de un reinicio, el primer &lt;code&gt;memory_pool_alloc()&lt;/code&gt; devuelve el bloque &lt;code&gt;0&lt;/code&gt;, el siguiente devuelve el &lt;code&gt;1&lt;/code&gt;, y así en orden de direcciones. Si se construye la lista al revés el pool funciona igual, solo que entrega direcciones descendentes, lo cual es más difícil de leer cuando uno está mirando valores de punteros en un depurador.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;memory_pool_reset()&lt;/code&gt; reconstruye la lista completa sin preguntar quién tiene qué, así que invalida todos los punteros pendientes. &lt;code&gt;used&lt;/code&gt; vuelve a cero, mientras que &lt;code&gt;high_water&lt;/code&gt; deliberadamente no.&lt;/p&gt;

&lt;h3&gt;
  
  
  Función de inicialización
&lt;/h3&gt;

&lt;p&gt;La función de inicialización es nuestro punto de entrada para empezar a usar el patrón. Acepta como parámetros: &lt;code&gt;memory_pool_t *mp&lt;/code&gt;, &lt;code&gt;void *storage&lt;/code&gt;, &lt;code&gt;size_t storage_size&lt;/code&gt;, &lt;code&gt;size_t block_size&lt;/code&gt;, &lt;code&gt;size_t block_align&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;storage_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;block_align&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;is_power_of_two&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_align&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_align&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;_Alignof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;block_align&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;_Alignof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;stride&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;round_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_align&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stride&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;aligned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;round_up&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_align&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;aligned&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;storage_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;usable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage_size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usable&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;stride&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stride&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_water&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;memory_pool_reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lo primero al pasar o manejar punteros es asegurarse de que no sean &lt;code&gt;NULL&lt;/code&gt;. Un puntero nulo significa que estaríamos escribiendo en la dirección de memoria cero. Como ya dijimos, en una computadora eso es un fallo de segmentación, pero aquí podríamos escribir en la dirección cero y corromper una dirección no intencionada. La verificación también comprueba explícitamente que &lt;code&gt;block_align&lt;/code&gt; sea potencia de dos, ya que &lt;code&gt;round_up&lt;/code&gt; se llama dos veces más abajo y solo funciona bajo esa garantía. La última verificación es &lt;code&gt;block_size &amp;lt; sizeof(void *)&lt;/code&gt;. Cualquier bloque que entreguemos tiene que ser lo bastante grande para guardar un enlace de la lista de libres mientras está libre, así que un bloque más chico que un puntero derramaría su enlace sobre el bloque de al lado.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ajuste de alineación: &lt;code&gt;block_align&lt;/code&gt; se sube a &lt;code&gt;_Alignof(void *)&lt;/code&gt; si se pide menos. Está permitido pedir alineación de 1 byte, pero la lista de libres guarda un puntero dentro de cada bloque libre, así que el pool entrega en silencio alineación de puntero en su lugar. Misma razón que la guarda de &lt;code&gt;block_size&lt;/code&gt;, desde el otro lado. Conviene explicitar lo que se desprende de esto: como &lt;code&gt;base&lt;/code&gt; está alineado y el stride es múltiplo de &lt;code&gt;block_align&lt;/code&gt;, todos los bloques del pool quedan alineados, no solo el primero. Esa es la garantía en la que uno se apoya al meter un struct en un bloque, así que hay que pasar &lt;code&gt;_Alignof(T)&lt;/code&gt; para un pool de &lt;code&gt;T&lt;/code&gt;. El ajuste por sí solo únicamente promete alineación de puntero.&lt;/li&gt;
&lt;li&gt;Guarda de desbordamiento: &lt;code&gt;if (stride &amp;lt; block_size)&lt;/code&gt; parece inalcanzable, ya que redondear hacia arriba no puede hacer un número más chico. Lo que atrapa es la vuelta al inicio: &lt;code&gt;round_up&lt;/code&gt; suma &lt;code&gt;a - 1&lt;/code&gt; antes de enmascarar, y si &lt;code&gt;block_size&lt;/code&gt; está cerca de &lt;code&gt;SIZE_MAX&lt;/code&gt; esa suma da la vuelta. Es el desbordamiento sin signo mordiéndonos otra vez, esta vez atrapado a propósito.&lt;/li&gt;
&lt;li&gt;Corrimiento de la base: el búfer de quien llama puede no empezar en una dirección alineada, así que &lt;code&gt;base&lt;/code&gt; se redondea hacia arriba y los bytes iniciales se pierden. Eso es &lt;code&gt;offset&lt;/code&gt;, y por eso &lt;code&gt;base&lt;/code&gt; y &lt;code&gt;storage&lt;/code&gt; pueden diferir.&lt;/li&gt;
&lt;li&gt;Cálculo de la capacidad: &lt;code&gt;usable / stride&lt;/code&gt;, descartando el resto. Entre el corrimiento y el truncamiento, la capacidad suele ser menor que &lt;code&gt;storage_size / block_size&lt;/code&gt;, y por eso existe &lt;code&gt;memory_pool_capacity()&lt;/code&gt; en vez de que quien llama la calcule. Si uno alinea su propio almacenamiento, con &lt;code&gt;alignas&lt;/code&gt; en el arreglo puesto al menos tan grande como el &lt;code&gt;block_align&lt;/code&gt; que pasa, el corrimiento no cuesta nada. Si se deja al azar, se puede perder un bloque en silencio.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Funciones de reserva y liberación
&lt;/h3&gt;

&lt;p&gt;Ahora que inicializamos el pool de memoria podemos empezar a usarlo. A &lt;code&gt;memory_pool_alloc()&lt;/code&gt; hay que pasarle el parámetro &lt;code&gt;memory_pool_t *mp&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;memory_pool_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_water&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_water&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hacemos nuestra verificación estándar de que &lt;code&gt;mp&lt;/code&gt; no sea &lt;code&gt;NULL&lt;/code&gt;, y junto a ella verificamos &lt;code&gt;mp-&amp;gt;free_head&lt;/code&gt;. Esa segunda es la forma en que un pool agotado se anuncia: que la lista de libres esté vacía es que la cabeza sea &lt;code&gt;NULL&lt;/code&gt;, así que no hace falta un helper &lt;code&gt;is_empty&lt;/code&gt; aparte como sí lo necesitaba el búfer circular. Devolver &lt;code&gt;NULL&lt;/code&gt; aquí también coincide con &lt;code&gt;malloc()&lt;/code&gt;, así que quien ya maneja una reserva fallida no tiene que aprender una convención nueva. Si ambas verificaciones pasan, creamos un puntero nuevo &lt;code&gt;p&lt;/code&gt; y lo apuntamos a &lt;code&gt;mp-&amp;gt;free_head&lt;/code&gt;. Ese es el bloque que estamos por devolverle a quien llama, y antes de soltarlo llamamos a &lt;code&gt;read_next(p)&lt;/code&gt; para sacar el enlace de sus primeros bytes, que es lo que pasa a ser la nueva cabeza. Lo último que hacemos es contabilidad: &lt;code&gt;mp-&amp;gt;used++&lt;/code&gt; registra que hay un bloque más entregado, y si eso empuja &lt;code&gt;used&lt;/code&gt; por encima de &lt;code&gt;mp-&amp;gt;high_water&lt;/code&gt; subimos el pico con él. Esa comparación es el único lugar donde &lt;code&gt;high_water&lt;/code&gt; cambia.&lt;/p&gt;

&lt;p&gt;Sigue &lt;code&gt;memory_pool_free()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;memory_pool_owns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;write_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Devolver un bloque es la imagen espejo. La primera verificación le pasa el puntero a &lt;code&gt;memory_pool_owns()&lt;/code&gt;, tema de la siguiente sección, que responde si esto es realmente el inicio de un bloque de este pool. La segunda, &lt;code&gt;mp-&amp;gt;used == 0u&lt;/code&gt;, rechaza una liberación en un pool que no tiene nada pendiente. Ambas se ejecutan antes de escribir nada, así que una liberación rechazada deja el pool completamente intacto, lista de libres incluida. El orden de esas dos tampoco es intercambiable, ya que &lt;code&gt;mp-&amp;gt;used&lt;/code&gt; dereferencia &lt;code&gt;mp&lt;/code&gt; y solo es seguro porque &lt;code&gt;memory_pool_owns()&lt;/code&gt; ya rechazó un pool nulo. Si se invierten para fallar primero en la verificación más barata, lo que se escribió es una dereferencia nula.&lt;/p&gt;

&lt;p&gt;Después empujamos. &lt;code&gt;write_next(block, mp-&amp;gt;free_head)&lt;/code&gt; escribe la cabeza actual dentro del bloque devuelto, &lt;code&gt;mp-&amp;gt;free_head = block&lt;/code&gt; hace de ese bloque la nueva cabeza, y &lt;code&gt;mp-&amp;gt;used--&lt;/code&gt; baja la cuenta. Este es el comportamiento de pila del que hablábamos antes: el bloque que se acaba de devolver es el que va a entregar el siguiente &lt;code&gt;memory_pool_alloc()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hay un caso que estas verificaciones no pueden atrapar: liberar el mismo bloque dos veces. Volveremos a eso después de la implementación.&lt;/p&gt;

&lt;h3&gt;
  
  
  El pool es dueño de la dirección de memoria
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_owns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta es la validación en la que se apoya &lt;code&gt;memory_pool_free()&lt;/code&gt;, y hace dos preguntas. La primera es si el puntero está dentro de la zona del pool siquiera, comparándolo contra &lt;code&gt;base&lt;/code&gt; y &lt;code&gt;end&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;La segunda es la interesante: &lt;code&gt;(addr - base) % mp-&amp;gt;block_size == 0u&lt;/code&gt; verifica que el puntero esté exactamente en un límite de bloque. Un puntero al medio de un bloque pasa la prueba de rango pero no es un bloque, y liberarlo insertaría un nodo falso en la lista de libres a un desplazamiento, así que la siguiente reserva entregaría un bloque que se solapa con su vecino. Esta operación módulo es la única división de todo el módulo, y es el mismo costo que evitamos en el búfer circular. Hacer que el stride sea potencia de dos la vuelve a convertir en una máscara.&lt;/p&gt;

&lt;h3&gt;
  
  
  Función de capacidad
&lt;/h3&gt;

&lt;p&gt;Conocer la capacidad es importante porque a partir de ella podemos identificar cuánto del pool de memoria tenemos para entregar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hacemos nuestra verificación de &lt;code&gt;NULL&lt;/code&gt; sobre &lt;code&gt;mp&lt;/code&gt;. Si es nulo devolvemos &lt;code&gt;0u&lt;/code&gt;, de lo contrario devolvemos la capacidad derivada allá en &lt;code&gt;memory_pool_init()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Funciones auxiliares
&lt;/h3&gt;

&lt;p&gt;Estas son las funciones para acceder a los miembros de &lt;code&gt;memory_pool_t&lt;/code&gt; sin modificarlos. Empezamos con &lt;code&gt;memory_pool_used()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_used&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aquí solo verificamos que el &lt;code&gt;mp&lt;/code&gt; pasado como argumento no sea &lt;code&gt;NULL&lt;/code&gt;, y si no lo es devolvemos el campo que pedimos, en este caso &lt;code&gt;mp-&amp;gt;used&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_available&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_high_water&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_water&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aquí está el resto de las funciones auxiliares, siguiendo la misma verificación de &lt;code&gt;NULL&lt;/code&gt; y devolviendo el valor que pedimos. Nótese que &lt;code&gt;memory_pool_available()&lt;/code&gt; calcula &lt;code&gt;capacity - used&lt;/code&gt; en lugar de recorrer la lista de libres para contar lo que queda, que es la misma respuesta sin el recorrido.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ejemplo de uso
&lt;/h2&gt;

&lt;p&gt;Ahora que explicamos la implementación completa, veamos cómo vamos a usar el patrón.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int16_t&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;sensor_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;sample_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;alignas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;sample_storage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_t&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="n"&gt;sample_pool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;sample_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;sensor_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memory_pool_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sample_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample_storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt; &lt;span class="n"&gt;sample_storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_t&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;_Alignof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_t&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;sensor_take_reading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;sensor_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memory_pool_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sample_pool&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_adc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sensor_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sensor_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sensor_flush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;transmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;memory_pool_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sample_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este ejemplo muestra que vamos a usar el pool de memoria para guardar &lt;code&gt;sample_t&lt;/code&gt;, que está compuesto por &lt;code&gt;timestamp&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt; y &lt;code&gt;sensor_id&lt;/code&gt;. Nótese que el bloque se reserva en una función y se libera en otra. Ese es el punto del pool: la memoria sobrevive a la llamada que la pidió, que es lo que permite que una lectura se quede ahí hasta que algo más esté listo para ocuparse de ella. Un solo espacio mantiene corto el ejemplo; un llamador real tiene varios a la vez.&lt;/p&gt;

&lt;p&gt;Ahora el &lt;code&gt;alignas(void *)&lt;/code&gt; sobre &lt;code&gt;sample_storage&lt;/code&gt;. A &lt;code&gt;init&lt;/code&gt; le pasamos &lt;code&gt;_Alignof(sample_t)&lt;/code&gt;, pero &lt;code&gt;init&lt;/code&gt; lo sube a &lt;code&gt;_Alignof(void *)&lt;/code&gt;, porque todo bloque libre tiene que poder guardar un puntero. El almacenamiento tiene que satisfacer al mayor de los dos, y aquí ese es &lt;code&gt;void *&lt;/code&gt;: en un host de 64 bits son 8 contra los 4 de &lt;code&gt;sample_t&lt;/code&gt;, y en un objetivo de 32 bits los dos son iguales, así que alinear al puntero cubre ambos casos. Si uno se equivoca, &lt;code&gt;base&lt;/code&gt; se corre hacia adelante y se pierde un bloque en silencio. Nótese que declaramos &lt;code&gt;sample_storage&lt;/code&gt; como &lt;code&gt;uint8_t&lt;/code&gt;, así que el arreglo son bytes crudos y podemos dimensionarlo multiplicando la cantidad de bloques por &lt;code&gt;sizeof(sample_t)&lt;/code&gt;. Ojo que &lt;code&gt;alignas&lt;/code&gt; necesita &lt;code&gt;&amp;lt;stdalign.h&amp;gt;&lt;/code&gt; en C11, y el fragmento omite los includes.&lt;/p&gt;

&lt;p&gt;Nótese también que el pool no tiene idea de a qué sensor pertenece un bloque. &lt;code&gt;sensor_id&lt;/code&gt; es un campo que pusimos nosotros en el struct, no algo que el pool registre. Lo único que supo siempre fue el tamaño de bloque y la alineación que le entregamos en &lt;code&gt;init&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nótese que &lt;code&gt;alloc&lt;/code&gt;, &lt;code&gt;free&lt;/code&gt;, y la forma en que usamos la memoria entre medio tienen la misma forma que &lt;code&gt;malloc()&lt;/code&gt; y &lt;code&gt;free()&lt;/code&gt;. Pedir memoria, verificar &lt;code&gt;NULL&lt;/code&gt;, usarla, devolverla. Eso es deliberado: el código escrito contra el heap se porta cambiando dos puntos de llamada. Dos diferencias que conviene conocer: &lt;code&gt;memory_pool_free()&lt;/code&gt; devuelve un &lt;code&gt;bool&lt;/code&gt; en lugar de nada, y liberar el mismo bloque dos veces no se comporta como lo haría el asignador del heap. Vamos a eso ahora.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caso límite: doble liberación
&lt;/h2&gt;

&lt;p&gt;Allá en &lt;code&gt;memory_pool_free()&lt;/code&gt; dijimos que había un caso que las verificaciones no pueden atrapar. Liberar en un pool vacío lo rechaza &lt;code&gt;mp-&amp;gt;used == 0u&lt;/code&gt;, y un puntero que no es el inicio de un bloque lo rechaza &lt;code&gt;memory_pool_owns()&lt;/code&gt;. Pero liberar un bloque que &lt;em&gt;ya&lt;/em&gt; está en la lista de libres pasa ambas. Es un inicio de bloque legítimo, así que &lt;code&gt;owns&lt;/code&gt; dice que sí, y hay otros bloques pendientes, así que &lt;code&gt;used&lt;/code&gt; no es cero.&lt;/p&gt;

&lt;p&gt;Así que el empuje se ejecuta. Escribimos &lt;code&gt;free_head&lt;/code&gt; dentro del bloque y luego hacemos de ese bloque la nueva cabeza. Si ese bloque ya era la cabeza, ahora se apunta a sí mismo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Antes:
  free_head -&amp;gt; [B] -&amp;gt; [C] -&amp;gt; NULL

memory_pool_free(mp, B) por segunda vez:
  write_next(B, free_head) el next de B ahora apunta a B
  free_head = B

Después:
  free_head -&amp;gt; [B] -&amp;gt; [B] -&amp;gt; [B] -&amp;gt; ...

  alloc() devuelve B
  alloc() devuelve B otra vez, a otro llamador

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nada reporta esto. &lt;code&gt;memory_pool_free()&lt;/code&gt; devuelve &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;used&lt;/code&gt; se ve plausible, y toda consulta dice que el pool está sano. Uno lo encuentra después, en el código que tuvo la mala suerte de ser el segundo dueño del bloque.&lt;/p&gt;

&lt;p&gt;Hay formas de atraparlo, y ambas cuestan algo por lo cual elegimos este patrón. Podríamos recorrer la lista de libres en cada liberación y rechazar un bloque que ya esté en ella, lo cual es correcto y convierte una liberación de tiempo constante en un recorrido. Podríamos mantener un mapa de bits aparte con los bloques reservados, lo cual cuesta memoria y renuncia a la propiedad de cero sobrecarga que motivó la lista intrusiva en primer lugar. Para una base de código de firmware pequeña, donde los puntos de reserva son pocos y revisables, documentarlo es una decisión defendible, y la limitación está escrita en el header justo al lado de la función que no puede atraparla. Para una base de código más grande valdría la pena pagar una verificación en la compilación de depuración.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cierre
&lt;/h2&gt;

&lt;p&gt;Eso es un pool de memoria completo. No hace reserva dinámica, entrega bloques y los recupera en tiempo constante sin búsquedas, no lleva ni un byte de sobrecarga por bloque, no puede fragmentarse por construcción, y después dice cuánto de él hacía falta realmente.&lt;/p&gt;

&lt;p&gt;Los sacrificios, todos deliberados:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Un solo tamaño por pool.&lt;/strong&gt; Cuatro tipos de objeto significan cuatro pools. Esto es lo que compra la ausencia de fragmentación, y hace que el pool sea mala opción para datos de tamaño genuinamente variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No es thread-safe ni ISR-safe.&lt;/strong&gt; A diferencia del búfer circular, aquí no hay un camino sin bloqueos. Toda llamada que muta toca &lt;code&gt;free_head&lt;/code&gt;, y no hay forma de repartir eso entre dos contextos como sí se reparten &lt;code&gt;head&lt;/code&gt; y &lt;code&gt;tail&lt;/code&gt;. Un pool compartido necesita una sección crítica alrededor de cada llamada que muta, que es también la razón por la que no hay ningún &lt;code&gt;volatile&lt;/code&gt; en &lt;code&gt;memory_pool_t&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Los bloques no se ponen en cero.&lt;/strong&gt; Estas son semánticas de &lt;code&gt;malloc&lt;/code&gt;, no de &lt;code&gt;calloc&lt;/code&gt;. Un bloque recién entregado tiene un puntero viejo de la lista de libres en sus primeros bytes y lo que haya dejado el dueño anterior en el resto.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La doble liberación solo es parcialmente detectable.&lt;/strong&gt; Cubierto arriba, y la limitación más importante del módulo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La capacidad se deriva, no se da.&lt;/strong&gt; El redondeo del stride, el corrimiento por alineación y el resto de la cola le comen espacio. Hay que leerla de vuelta con &lt;code&gt;memory_pool_capacity()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dos patrones y ya empezaron a apoyarse uno en el otro. El búfer circular nos dio almacenamiento fijo para bytes, el pool nos da almacenamiento fijo para objetos, y la cola de eventos, más adelante en la serie, va a querer los dos: un anillo de punteros a bloques entregados por un pool. Así se consigue una cola de structs sin una sola llamada a &lt;code&gt;malloc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lo que sigue en la serie es la máquina de estados. El búfer circular y el pool trataron con almacenamiento, bytes en uno y bloques en el otro, y a ninguno le importaba qué significaba nada de eso. La máquina de estados es donde empieza a significar algo.&lt;/p&gt;

</description>
      <category>embeddedsystems</category>
      <category>memorymanagement</category>
      <category>c</category>
      <category>microcontrollers</category>
    </item>
    <item>
      <title>Common Embedded Patterns: Memory Pool</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Thu, 10 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/common-embedded-patterns-memory-pool-1db2</link>
      <guid>https://dev.to/elcapitan/common-embedded-patterns-memory-pool-1db2</guid>
      <description>&lt;p&gt;This is the second part of the series re-exploring common embedded patterns. This one focuses on the memory pool. This pattern is often used as an alternative to &lt;code&gt;malloc()&lt;/code&gt; and &lt;code&gt;free()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The complete, tested source lives in the companion repo: &lt;a href="https://github.com/ileanmjr88/tetzontli" rel="noopener noreferrer"&gt;ileanmjr88/tetzontli&lt;/a&gt;. Each pattern in this series is its own module with a full GoogleTest suite, so you can clone it and run the tests yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Concept
&lt;/h2&gt;

&lt;p&gt;Let's start with a quick comparison to the ring buffer pattern. Both patterns are used to store data, but they differ in their point of view of going about it. We refer to the ring buffer as a container. Each item inserted into the data structure is homogeneous and uniform, and it owns the order of that data: you push in, you pop out, following FIFO. The buffer is aware of what's inside it, at least in terms of size and sequence.&lt;/p&gt;

&lt;p&gt;The memory pool takes the opposite point of view. It isn't a container, it's custom memory management. It doesn't know the structure of the data being stored, it doesn't care what's in the data, and it doesn't track the sequence. You ask for a block, it checks if one is available, and if it is, it hands you a pointer to that block. From the memory pool's point of view it only sees blocks as occupied or free, with no reference to their structure or the order in which they were handed out.&lt;/p&gt;

&lt;p&gt;At this point you're probably wondering what a memory pool is in the simplest terms. It's a buffer of equal-sized memory blocks, typically implemented as an array, so we have a chunk or pool of memory we can grab from. Our &lt;code&gt;init&lt;/code&gt; determines the size of our blocks, and initializes the manager structure that keeps track of where the next available block is located.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Look at &lt;code&gt;malloc()&lt;/code&gt; and &lt;code&gt;free()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Before diving into the memory pool itself, it's worth stepping back and recalling what &lt;code&gt;malloc()&lt;/code&gt; and &lt;code&gt;free()&lt;/code&gt; actually do. This is important since we'll be implementing similar functionality. The difference is that we pre-allocate a fixed buffer up front, usually a static array, while these functions dynamically allocate and deallocate memory on the heap.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;malloc()&lt;/code&gt; asks the heap allocator for a chunk of memory, sized for the item you're trying to store. If the allocation is successful, meaning the heap allocator found space, it returns a pointer. If it's unable to find any space it returns &lt;code&gt;NULL&lt;/code&gt;, indicating failure. The heap allocator manages this by keeping track of free and used chunks internally. Each time we use &lt;code&gt;malloc()&lt;/code&gt; it keeps track of how much we used, and the same goes for &lt;code&gt;free()&lt;/code&gt;: each time we free memory it marks it as available. &lt;code&gt;free()&lt;/code&gt; does the reverse of &lt;code&gt;malloc()&lt;/code&gt;. We call it with the pointer we want to give back to the heap allocator, it marks that chunk as free, and moves on.&lt;/p&gt;

&lt;p&gt;These two functions for dynamically allocating and deallocating memory work great on modern computers and in server farms where they have large amounts of memory and a large address space. If a &lt;code&gt;malloc()&lt;/code&gt; takes a microsecond longer to find a chunk of memory it's not a big deal. Embedded systems don't get to make those same assumptions, and that's where the cracks start to show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fragmentation&lt;/strong&gt; : as we allocate and deallocate chunks of mixed sizes, the free memory left behind stops being contiguous. Eventually a request fails even though there are more than enough free bytes in total, just not enough of them next to each other. The failure is late, intermittent, and not reproducible on the bench.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-deterministic timing&lt;/strong&gt; : not every embedded system needs deterministic timing, but for the ones that do, &lt;code&gt;malloc()&lt;/code&gt; is a problem. The allocator searches for a chunk that fits, so how long it takes depends on the state of the heap. That's hard to accept in a hard-deadline loop, and impossible to budget for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Late failure&lt;/strong&gt; : you learn you're out of memory at the exact moment you need memory, which is usually the moment you can least do anything about it. There's no boot-time or link-time answer to "will this fit."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keeping track of the available blocks
&lt;/h2&gt;

&lt;p&gt;While we aren't using &lt;code&gt;malloc()&lt;/code&gt; or &lt;code&gt;free()&lt;/code&gt;, it also means we aren't using the built-in heap manager that kept its own record of which chunks were free and which were handed out. Our implementation needs to do that bookkeeping itself. There are really only two things it has to know: whether any block is available at all, and if so, where one is.&lt;/p&gt;

&lt;p&gt;Before we get into the implementation used in the tetzontli repo, let's briefly explore two common approaches to this bookkeeping. The first is the bitmap, where each bit of an integer is used to indicate whether a block is in use or free. The number of blocks determines the integer type used. The alternative is the free list method, which threads the available blocks into a linked list, keeping a tracker pointer aimed at the next block that's available. When we deallocate, the block goes back onto the tracker pointer. There is one clear advantage to the free list method: it never has to search. Taking a block means grabbing whatever the tracker pointer is aimed at, and giving one back means putting it at the front. Both are a single pointer update, no matter how full the pool is. The bitmap has to scan for a spare bit, and how long that takes depends on how many blocks are already in use. The bitmap does have one advantage we are giving up though: because it records the state of every block, it can tell a double free from a legitimate one. The free list cannot, and we will come back to what that costs us.&lt;/p&gt;

&lt;p&gt;The tetzontli implementation uses the intrusive free list approach. It's a common variation on the free list where each free block itself knows where the next available chunk of memory is located. The reason for this approach is that there's no need to keep a bitmap to keep track of the available blocks, since our &lt;code&gt;next&lt;/code&gt; pointer already knows the next available block. Also a reminder, since we aren't allocating memory dynamically, we're using an array as the container to create the pool of memory. That makes traversal easier after &lt;code&gt;init&lt;/code&gt;. Once we start to allocate and deallocate, we want the most recently deallocated blocks to be the ones we hand out first, keeping it like a stack, which is friendlier to cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Memory pool structure
&lt;/h3&gt;

&lt;p&gt;Similar to the ring buffer, we will create a struct. This is a simple way to keep all the parameters together and accessible from a single variable pointer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.h&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;high_water&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;uint8_t *base&lt;/code&gt; : the address of block &lt;code&gt;0&lt;/code&gt; , and the anchor the entire pool is measured from. Every block lives at &lt;code&gt;base + i * block_size&lt;/code&gt; . It is tempting to read this as the first byte of the buffer you hand to the pool, and most of the time that's exactly what it is, but the two can come apart, for a reason we'll get into in &lt;code&gt;memory_pool_init()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size_t block_size&lt;/code&gt;: this is what we will often call the stride. It defines the size of each memory block and how far apart they are from block to block.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size_t capacity&lt;/code&gt;: this is the number of blocks in the pool. Fixed at &lt;code&gt;memory_pool_init()&lt;/code&gt; and never changes. How many are free at any moment is &lt;code&gt;capacity - used&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size_t used&lt;/code&gt;: the number of blocks currently handed out. Incremented by &lt;code&gt;memory_pool_alloc()&lt;/code&gt;, decremented by &lt;code&gt;memory_pool_free()&lt;/code&gt;. Note handed out, not in use. The pool has no idea what you're doing with a block, only whether it has given it away and not gotten it back.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size_t high_water&lt;/code&gt; : the peak value &lt;code&gt;used&lt;/code&gt; has ever reached since &lt;code&gt;memory_pool_init()&lt;/code&gt;. This is the number you size the pool from: run the system under worst-case load, read it back, and you know how many blocks you actually needed. It deliberately survives &lt;code&gt;memory_pool_reset()&lt;/code&gt;. Handing every block back doesn't change the fact that you once needed that many at once, and clearing the peak would throw away the measurement. Only a fresh &lt;code&gt;memory_pool_init()&lt;/code&gt; starts it over.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;void *free_head&lt;/code&gt;: the head of the free list. The first available block to hand out, or &lt;code&gt;NULL&lt;/code&gt; when every block is outstanding. The list is intrusive, meaning its links live inside the free blocks themselves rather than in separate node structs. Each free block stores a pointer to the next one in its own first bytes. That's what makes the free list cost zero extra memory, and it's why &lt;code&gt;memory_pool_init()&lt;/code&gt; refuses a &lt;code&gt;block_size&lt;/code&gt; smaller than a pointer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Round-up function
&lt;/h3&gt;

&lt;p&gt;We will leverage the &lt;code&gt;is_power_of_two()&lt;/code&gt; in this pattern as well but we will also add a new method to round up to the next multiple of &lt;code&gt;a&lt;/code&gt;. We covered &lt;code&gt;is_power_of_two()&lt;/code&gt; in detail in the ring buffer post, so we won't repeat it here. It rejects zero as well as non-powers of two, which is what keeps &lt;code&gt;block_align = 0&lt;/code&gt; from ever reaching &lt;code&gt;round_up()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;round_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1u&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1u&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;(x + (a - 1u))&lt;/code&gt; : This takes the variable &lt;code&gt;x&lt;/code&gt;, the number we want to round up, and the variable &lt;code&gt;a&lt;/code&gt;, the alignment we want to round it up to. Adding &lt;code&gt;a - 1&lt;/code&gt; first is what pushes any value that isn't already on a boundary past the next one.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~(a - 1u)&lt;/code&gt; : this takes the number we want to align by, subtracts one, and does a bitwise not, which gives us a mask that clears the low bits and leaves everything above them alone.&lt;/li&gt;
&lt;li&gt;Taking both and doing the bitwise &lt;code&gt;&amp;amp;&lt;/code&gt; drops the value back down onto a boundary. On its own that would round down. Adding &lt;code&gt;a - 1&lt;/code&gt; first is what turns it into a round up. This can be confusing at first glance.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;caller: round_up(x = 12, a = 16);

- (x + (a - 1u)) = (12 + (16 - 1u)) = (12 + 15) = 27 = 0b00011011
- (a - 1u) = (16 - 1u) = 15

Taking the evaluation further on the right hand side of the &amp;amp;
- ~(15) = ~(0b00001111) = 0b11110000

Now putting both sides together 
- 0b00011011 &amp;amp; 0b11110000 = 0b00010000 = 16

Meaning we need to round up 12 to 16

And when the value is already on a boundary, nothing moves:
- round_up(16, 16) = (16 + 15) &amp;amp; ~15
- 0b00011111 &amp;amp; 0b11110000 = 0b00010000 = 16

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the ring buffer, the masking trick only worked because the capacity was a power of two. The same rule applies here, except it lands on &lt;code&gt;a&lt;/code&gt;, the alignment we are rounding up to, and not on the capacity. &lt;code&gt;a&lt;/code&gt; needs to be a power of two, which is why &lt;code&gt;memory_pool_init()&lt;/code&gt; runs &lt;code&gt;is_power_of_two()&lt;/code&gt; on it before doing any rounding at all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Example when a is not a power of two.

- round_up(12, 12) = (12 + 11) &amp;amp; ~11 = 23 &amp;amp; ~11
- 0b00010111 &amp;amp; ~0b00001011 = 0b00010111 &amp;amp; 0b11110100 = 0b00010100 = 20

It should yield 12, since 12 is already a multiple of 12 and a correct
round up would leave it alone. The reason it breaks is that 11 is
0b00001011, not a clean run of ones, so the mask clears the wrong bits.
This is why the power of two is used here to be able to optimize the round_up.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Read next and write next functions
&lt;/h3&gt;

&lt;p&gt;This pair of functions are the ones that will read which is the next available block and update the next available block of memory. Let's start with &lt;code&gt;read_next()&lt;/code&gt;, for each allocation we will need to read where the next block to allocate is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;read_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;slot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function takes the pointer &lt;code&gt;block&lt;/code&gt;, which arrives as a &lt;code&gt;const void *&lt;/code&gt;, and casts it to &lt;code&gt;void *const *&lt;/code&gt;. That cast reads right to left: &lt;code&gt;slot&lt;/code&gt; is a pointer to a constant pointer to &lt;code&gt;void&lt;/code&gt;. The &lt;code&gt;const&lt;/code&gt; applies to the pointer stored inside the block, not to &lt;code&gt;slot&lt;/code&gt; itself, and it's there so the cast doesn't quietly throw away the qualifier the parameter already carried. We create a variable &lt;code&gt;void *next&lt;/code&gt; as a container to copy into. We do a memory copy of &lt;code&gt;sizeof next&lt;/code&gt; bytes out of the slot and into &lt;code&gt;next&lt;/code&gt;. This is how we return a memory pointer of the next block of data that is available.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;memcpy&lt;/code&gt; is worth pausing on, because the obvious version is a single line, &lt;code&gt;return *(void **)block;&lt;/code&gt;. The problem is that those bytes live inside a &lt;code&gt;uint8_t&lt;/code&gt; array, so reading them through a &lt;code&gt;void**&lt;/code&gt; means accessing an object through a pointer of a different type, which is what the aliasing rules forbid. The compiler is allowed to assume we didn't do that. &lt;code&gt;memcpy&lt;/code&gt; is the sanctioned way to say copy these bytes as they are without making a claim about types, and it costs nothing at any optimization level above &lt;code&gt;-O0&lt;/code&gt;: every compiler recognizes a pointer-sized &lt;code&gt;memcpy&lt;/code&gt; and emits a single load or store. In a debug build it stays a real call, which is worth knowing but not worth changing the code over.&lt;/p&gt;

&lt;p&gt;Next, is &lt;code&gt;write_next()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;write_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;slot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function mirrors &lt;code&gt;read_next()&lt;/code&gt;. The difference is that we take in &lt;code&gt;void *block&lt;/code&gt; and declare &lt;code&gt;slot&lt;/code&gt; as a &lt;code&gt;void **&lt;/code&gt;, assigning it the cast of &lt;code&gt;block&lt;/code&gt;, which tells the compiler to treat those first bytes as a place where a pointer lives. Then we memory copy &lt;code&gt;sizeof next&lt;/code&gt; bytes from &lt;code&gt;&amp;amp;next&lt;/code&gt;, the address of our local, into the slot. What lands in the block is the pointer value itself. Neither of these two functions ever looks at &lt;code&gt;block_size&lt;/code&gt;, they only touch the first &lt;code&gt;sizeof(void *)&lt;/code&gt; bytes, which is why the free list costs the same no matter how large the blocks are.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reset function
&lt;/h3&gt;

&lt;p&gt;Before we get to the &lt;code&gt;init&lt;/code&gt; function, it's worth talking about how we reset the memory pool. This does the job of relinking the blocks of memory in sequential order, which makes it handy to reuse inside &lt;code&gt;init&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;write_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start with a quick check that we didn't pass a &lt;code&gt;NULL&lt;/code&gt; pointer to the function. Without it, the very next line, &lt;code&gt;mp-&amp;gt;free_head = NULL&lt;/code&gt;, would dereference that null handle. On a desktop that's a segmentation fault. On most microcontrollers there's no MMU to catch it, so address zero is real memory, often the vector table, and the write quietly succeeds and corrupts something instead. If &lt;code&gt;mp&lt;/code&gt; is an actual memory address, we set &lt;code&gt;mp-&amp;gt;free_head = NULL&lt;/code&gt; so we know the state we're starting from before we assign the next pointer in each block.&lt;/p&gt;

&lt;p&gt;Then we iterate with &lt;code&gt;for (size_t i = mp-&amp;gt;capacity; i-- &amp;gt; 0;)&lt;/code&gt;. The decrement lives in the comparison so the check runs first and the decrement second, which means the body sees &lt;code&gt;capacity - 1&lt;/code&gt; down to &lt;code&gt;0&lt;/code&gt;, and the loop exits after &lt;code&gt;0&lt;/code&gt; has run. The obvious version, &lt;code&gt;for (size_t i = capacity - 1; i &amp;gt;= 0; i--)&lt;/code&gt;, never terminates. &lt;code&gt;size_t&lt;/code&gt; is unsigned, so &lt;code&gt;i &amp;gt;= 0&lt;/code&gt; is always true, and decrementing &lt;code&gt;0&lt;/code&gt; wraps around to &lt;code&gt;SIZE_MAX&lt;/code&gt; rather than going negative. This is the same unsigned wraparound we leaned on in the ring buffer's &lt;code&gt;count&lt;/code&gt;, working against us here instead of for us.&lt;/p&gt;

&lt;p&gt;The direction matters for a second reason. Each pass writes the current head into a block and then makes that block the new head, so walking the array backwards produces a list that comes out forwards. After a reset the first &lt;code&gt;memory_pool_alloc()&lt;/code&gt; returns block &lt;code&gt;0&lt;/code&gt;, the next returns block &lt;code&gt;1&lt;/code&gt;, and so on in address order. Build the list the other way and the pool works identically, it just hands out descending addresses, which is harder to read when you are staring at pointer values in a debugger.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;memory_pool_reset()&lt;/code&gt; rebuilds the whole list without asking who is holding what, so it invalidates every outstanding pointer. &lt;code&gt;used&lt;/code&gt; goes back to zero, while &lt;code&gt;high_water&lt;/code&gt; deliberately does not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialization function
&lt;/h3&gt;

&lt;p&gt;The initialization function is our entry point of actually starting to use this pattern. It will accept as parameters: &lt;code&gt;memory_pool_t *mp&lt;/code&gt;, &lt;code&gt;void *storage&lt;/code&gt;, &lt;code&gt;size_t storage_size&lt;/code&gt;, &lt;code&gt;size_t block_size&lt;/code&gt;, &lt;code&gt;size_t block_align&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;storage_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;block_align&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;is_power_of_two&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_align&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_align&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;_Alignof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;block_align&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;_Alignof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;stride&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;round_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_align&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stride&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;aligned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;round_up&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_align&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;aligned&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;storage_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;usable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage_size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usable&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;stride&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stride&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_water&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;memory_pool_reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing when passing or dealing with pointers is to make sure they are not &lt;code&gt;NULL&lt;/code&gt;. A null pointer means we would be writing to memory address zero. As stated before, on a computer that's a segmentation fault, but here we could write at address zero and corrupt an unintended address. The check also verifies explicitly that &lt;code&gt;block_align&lt;/code&gt; is a power of two, since &lt;code&gt;round_up&lt;/code&gt; is called twice further down and only works under that guarantee. The final check is &lt;code&gt;block_size &amp;lt; sizeof(void *)&lt;/code&gt;. Any block we hand out has to be big enough to hold a free list link while it's free, so a block smaller than a pointer would have its link spill into the block next to it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alignment clamp: &lt;code&gt;block_align&lt;/code&gt; gets raised to &lt;code&gt;_Alignof(void *)&lt;/code&gt; if you ask for less. You're allowed to request 1-byte alignment, but the free list stores a pointer inside every free block, so the pool quietly gives you pointer alignment instead. Same reason as the &lt;code&gt;block_size&lt;/code&gt; guard, from the other direction. It is worth spelling out what falls out of this: because &lt;code&gt;base&lt;/code&gt; is aligned and the stride is a multiple of &lt;code&gt;block_align&lt;/code&gt;, every block in the pool is aligned, not just the first one. That is the guarantee you are leaning on when you put a struct in a block, so pass &lt;code&gt;_Alignof(T)&lt;/code&gt; for a pool of &lt;code&gt;T&lt;/code&gt;. The clamp only promises pointer alignment on its own.&lt;/li&gt;
&lt;li&gt;Overflow guard: &lt;code&gt;if (stride &amp;lt; block_size)&lt;/code&gt; looks unreachable, since rounding up can't make a number smaller. It catches wraparound: &lt;code&gt;round_up&lt;/code&gt; adds &lt;code&gt;a - 1&lt;/code&gt; before masking, and if &lt;code&gt;block_size&lt;/code&gt; is near &lt;code&gt;SIZE_MAX&lt;/code&gt; that addition wraps. That is unsigned wraparound biting us again, this time caught on purpose.&lt;/li&gt;
&lt;li&gt;Base bump: the caller's buffer may not start on an aligned address, so &lt;code&gt;base&lt;/code&gt; gets rounded up and the lead-in bytes are lost. That's &lt;code&gt;offset&lt;/code&gt;, and it's why &lt;code&gt;base&lt;/code&gt; and &lt;code&gt;storage&lt;/code&gt; can differ.&lt;/li&gt;
&lt;li&gt;Capacity math: &lt;code&gt;usable / stride&lt;/code&gt;, with the remainder discarded. Between the bump and the truncation, capacity is usually less than &lt;code&gt;storage_size / block_size&lt;/code&gt;, which is why &lt;code&gt;memory_pool_capacity()&lt;/code&gt; exists rather than the caller computing it. Align your storage yourself, with &lt;code&gt;alignas&lt;/code&gt; on the array set at least as large as the &lt;code&gt;block_align&lt;/code&gt; you pass, and the bump costs you nothing. Leave it to chance and you can quietly lose a block.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Allocate and free functions
&lt;/h3&gt;

&lt;p&gt;Now that we have initialized the memory pool we can start using it. To &lt;code&gt;memory_pool_alloc()&lt;/code&gt; we will need to pass the parameter of &lt;code&gt;memory_pool_t *mp&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;memory_pool_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_water&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_water&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We do our standard check that &lt;code&gt;mp&lt;/code&gt; is not &lt;code&gt;NULL&lt;/code&gt;, and alongside it we check &lt;code&gt;mp-&amp;gt;free_head&lt;/code&gt;. That second one is how an exhausted pool announces itself: the free list being empty is the head being &lt;code&gt;NULL&lt;/code&gt;, so there is no need for a separate &lt;code&gt;is_empty&lt;/code&gt; helper the way the ring buffer needed one. Returning &lt;code&gt;NULL&lt;/code&gt; here also matches &lt;code&gt;malloc()&lt;/code&gt;, so a caller that already handles a failed allocation doesn't have to learn a new convention. If both checks pass, we create a new pointer &lt;code&gt;p&lt;/code&gt; and point it at &lt;code&gt;mp-&amp;gt;free_head&lt;/code&gt;. That is the block we are about to hand back to the caller, and before we let go of it we call &lt;code&gt;read_next(p)&lt;/code&gt; to pull the link out of its first bytes, which is what becomes the new head. The last thing we do is bookkeeping: &lt;code&gt;mp-&amp;gt;used++&lt;/code&gt; records that one more block is outstanding, and if that pushes &lt;code&gt;used&lt;/code&gt; past &lt;code&gt;mp-&amp;gt;high_water&lt;/code&gt; we move the peak up with it. That comparison is the only place &lt;code&gt;high_water&lt;/code&gt; ever changes.&lt;/p&gt;

&lt;p&gt;Next, &lt;code&gt;memory_pool_free()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;memory_pool_owns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;write_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;free_head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Giving a block back is the mirror image. The first check hands the pointer to &lt;code&gt;memory_pool_owns()&lt;/code&gt;, the subject of the next section, which answers whether this is actually the start of a block in this pool. The second, &lt;code&gt;mp-&amp;gt;used == 0u&lt;/code&gt;, rejects a free into a pool with nothing outstanding. Both run before we write anything, so a rejected free leaves the pool completely untouched, free list included. The order of those two is not interchangeable either, since &lt;code&gt;mp-&amp;gt;used&lt;/code&gt; dereferences &lt;code&gt;mp&lt;/code&gt; and is only safe because &lt;code&gt;memory_pool_owns()&lt;/code&gt; already turned away a null pool. Swap them to fail on the cheaper check first and you have written a null dereference.&lt;/p&gt;

&lt;p&gt;Then we push. &lt;code&gt;write_next(block, mp-&amp;gt;free_head)&lt;/code&gt; writes the current head into the returned block, &lt;code&gt;mp-&amp;gt;free_head = block&lt;/code&gt; makes that block the new head, and &lt;code&gt;mp-&amp;gt;used--&lt;/code&gt; brings the count down. This is the stack behavior from earlier: the block you just gave back is the one the next &lt;code&gt;memory_pool_alloc()&lt;/code&gt; hands out.&lt;/p&gt;

&lt;p&gt;There is one case these checks cannot catch, freeing the same block twice. We will come back to that after the implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory pool owns memory address
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_owns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the validation &lt;code&gt;memory_pool_free()&lt;/code&gt; leans on, and it asks two questions. The first is whether the pointer is inside the arena at all, comparing it against &lt;code&gt;base&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The second is the interesting one: &lt;code&gt;(addr - base) % mp-&amp;gt;block_size == 0u&lt;/code&gt; checks the pointer is exactly on a block boundary. A pointer into the middle of a block passes the range test but is not a block, and freeing it would splice a bogus node into the free list at an offset, so the next allocation would hand out a block overlapping its neighbor. This modulo is the one division in the module, and it is the same cost we avoided in the ring buffer. Making the stride a power of two turns it back into a mask.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capacity function
&lt;/h3&gt;

&lt;p&gt;Knowing the capacity is important because from that we can identify how much of the memory pool we have to hand out to be used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We do our &lt;code&gt;NULL&lt;/code&gt; check on &lt;code&gt;mp&lt;/code&gt;. If it is null we return &lt;code&gt;0u&lt;/code&gt;, otherwise we return the capacity derived back in &lt;code&gt;memory_pool_init()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Helper functions
&lt;/h3&gt;

&lt;p&gt;These are the functions to access the data members of the &lt;code&gt;memory_pool_t&lt;/code&gt; without modifying them. We will get started with &lt;code&gt;memory_pool_used()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_used&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we just check that the &lt;code&gt;mp&lt;/code&gt; passed as an argument is not &lt;code&gt;NULL&lt;/code&gt;, otherwise we return the field we are asking for, in this case &lt;code&gt;mp-&amp;gt;used&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/memory_pool/memory_pool.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_available&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;memory_pool_high_water&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;high_water&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have the remainder of the helper functions, following the same &lt;code&gt;NULL&lt;/code&gt; check and returning the value we asked for. Note that &lt;code&gt;memory_pool_available()&lt;/code&gt; works out &lt;code&gt;capacity - used&lt;/code&gt; rather than walking the free list to count what's left, which is the same answer without the traversal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example usage
&lt;/h2&gt;

&lt;p&gt;Now that we have explained the full implementation, let's see how we will use the pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int16_t&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;sensor_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;sample_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;alignas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;sample_storage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_t&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;memory_pool_t&lt;/span&gt; &lt;span class="n"&gt;sample_pool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;sample_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;sensor_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memory_pool_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sample_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample_storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt; &lt;span class="n"&gt;sample_storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_t&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;_Alignof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_t&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;sensor_take_reading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;sensor_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memory_pool_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sample_pool&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_adc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sensor_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sensor_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sensor_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sensor_flush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;transmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;memory_pool_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sample_pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example shows we will be using the memory pool to store &lt;code&gt;sample_t&lt;/code&gt;, which is made up of &lt;code&gt;timestamp&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt; and &lt;code&gt;sensor_id&lt;/code&gt;. Notice the block is allocated in one function and freed in another. That is the point of the pool, the memory outlives the call that asked for it, which is what lets a reading sit around until something else is ready to deal with it. One slot keeps the example short; a real caller holds several at once.&lt;/p&gt;

&lt;p&gt;Now &lt;code&gt;alignas(void *)&lt;/code&gt; on &lt;code&gt;sample_storage&lt;/code&gt;. We pass &lt;code&gt;_Alignof(sample_t)&lt;/code&gt; to &lt;code&gt;init&lt;/code&gt;, but &lt;code&gt;init&lt;/code&gt; clamps that up to &lt;code&gt;_Alignof(void *)&lt;/code&gt;, because every free block has to hold a pointer. The storage has to satisfy whichever of the two ends up larger, and here that's &lt;code&gt;void *&lt;/code&gt;, on a 64-bit host it's 8 against &lt;code&gt;sample_t&lt;/code&gt;'s 4, and on a 32-bit target the two are equal, so aligning to the pointer covers both. Get it wrong and &lt;code&gt;base&lt;/code&gt; gets bumped forward and you quietly lose a block. Notice we declare &lt;code&gt;sample_storage&lt;/code&gt; as &lt;code&gt;uint8_t&lt;/code&gt; so the array is raw bytes and we can size it by multiplying the block count by &lt;code&gt;sizeof(sample_t)&lt;/code&gt;. Note that &lt;code&gt;alignas&lt;/code&gt; needs &lt;code&gt;&amp;lt;stdalign.h&amp;gt;&lt;/code&gt; in C11, and the snippet leaves out the includes.&lt;/p&gt;

&lt;p&gt;Also notice the pool has no idea which sensor a block belongs to. &lt;code&gt;sensor_id&lt;/code&gt; is a field we put in the struct ourselves, not something the pool tracks. It only ever knew the block size and alignment we handed it at &lt;code&gt;init&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Notice that &lt;code&gt;alloc&lt;/code&gt;, &lt;code&gt;free&lt;/code&gt;, and the way we use the memory in between are the same shape as &lt;code&gt;malloc()&lt;/code&gt; and &lt;code&gt;free()&lt;/code&gt;. Ask for memory, check for &lt;code&gt;NULL&lt;/code&gt;, use it, hand it back. That is deliberate, code written against the heap ports over by changing two call sites. Two differences worth knowing: &lt;code&gt;memory_pool_free()&lt;/code&gt; returns a &lt;code&gt;bool&lt;/code&gt; instead of nothing, and freeing the same block twice does not behave the way your heap allocator would. We will get to that next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Double-free edge case
&lt;/h2&gt;

&lt;p&gt;Back in &lt;code&gt;memory_pool_free()&lt;/code&gt; we said there was one case the checks cannot catch. Freeing into an empty pool is rejected by &lt;code&gt;mp-&amp;gt;used == 0u&lt;/code&gt;, and a pointer that isn't a block start is rejected by &lt;code&gt;memory_pool_owns()&lt;/code&gt;. But freeing a block that is &lt;em&gt;already&lt;/em&gt; on the free list passes both. It is a legitimate block start, so &lt;code&gt;owns&lt;/code&gt; says yes, and other blocks are still outstanding, so &lt;code&gt;used&lt;/code&gt; is nonzero.&lt;/p&gt;

&lt;p&gt;So the push runs. We write &lt;code&gt;free_head&lt;/code&gt; into the block and then make the block the new head. If that block was already the head, it now points at itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:
  free_head -&amp;gt; [B] -&amp;gt; [C] -&amp;gt; NULL

memory_pool_free(mp, B) a second time:
  write_next(B, free_head) B's next now points at B
  free_head = B

After:
  free_head -&amp;gt; [B] -&amp;gt; [B] -&amp;gt; [B] -&amp;gt; ...

  alloc() returns B
  alloc() returns B again, to a different caller

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing reports this. &lt;code&gt;memory_pool_free()&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;used&lt;/code&gt; looks plausible, and every query says the pool is healthy. You find it later, in whatever code was unlucky enough to own the block second.&lt;/p&gt;

&lt;p&gt;There are ways to catch it and both cost something we chose this pattern for. We could walk the free list on every free and reject a block already on it, which is correct and turns a constant-time free into a scan. We could keep a side bitmap of allocated blocks, which costs memory and gives up the zero-overhead property that motivated the intrusive list in the first place. For a small firmware codebase where the allocation sites are few and reviewable, documenting it is a defensible trade, and the limitation is written into the header right next to the function that cannot catch it. For a larger codebase it would be worth paying for a debug-build check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;That's a complete memory pool. It does no dynamic allocation, hands blocks out and takes them back in constant time with no search, carries zero bytes of per-block overhead, cannot fragment by construction, and tells you afterwards how much of it you actually needed.&lt;/p&gt;

&lt;p&gt;The trades, all deliberate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One size per pool.&lt;/strong&gt; Four object types means four pools. This is what buys away fragmentation, and it makes the pool a poor fit for genuinely variable-sized data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not thread-safe or ISR-safe.&lt;/strong&gt; Unlike the ring buffer, there is no lock-free path here. Every mutating call touches &lt;code&gt;free_head&lt;/code&gt;, and there is no way to split that between two contexts the way &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; split. A shared pool needs a critical section around every mutating call, which is also why there is no &lt;code&gt;volatile&lt;/code&gt; anywhere in &lt;code&gt;memory_pool_t&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blocks aren't zeroed.&lt;/strong&gt; These are &lt;code&gt;malloc&lt;/code&gt; semantics, not &lt;code&gt;calloc&lt;/code&gt;. A fresh block holds a stale free list pointer in its first bytes and whatever the last owner left in the rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Double free is only partly detectable.&lt;/strong&gt; Covered above, and the most important limitation in the module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capacity is derived, not given.&lt;/strong&gt; Stride rounding, the alignment bump, and the tail remainder all eat into it. Read it back with &lt;code&gt;memory_pool_capacity()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two patterns in and they have started to lean on each other. The ring buffer gave us fixed storage for bytes, the pool gives us fixed storage for objects, and the event queue later in this series will want both: a ring of pointers to blocks handed out by a pool. That is how you get a queue of structs without a single call to &lt;code&gt;malloc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next in the series is the state machine. The ring buffer and the pool both dealt in storage, bytes in one and blocks in the other, and neither cared what any of it meant. The state machine is where it starts to mean something.&lt;/p&gt;

</description>
      <category>embeddedsystems</category>
      <category>memorymanagement</category>
      <category>c</category>
      <category>microcontrollers</category>
    </item>
    <item>
      <title>Common Embedded Patterns: Ring Buffer</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/common-embedded-patterns-ring-buffer-36jc</link>
      <guid>https://dev.to/elcapitan/common-embedded-patterns-ring-buffer-36jc</guid>
      <description>&lt;p&gt;This is the first part of a new series where I'll re-explore common embedded patterns, starting with the ring buffer. It's one of those patterns used in serial communication to make sure no frame of data is lost, and it's also a building block for other patterns we'll cover later.&lt;/p&gt;

&lt;p&gt;The complete, tested source lives in the companion repo: &lt;a href="https://github.com/ileanmjr88/tetzontli" rel="noopener noreferrer"&gt;ileanmjr88/tetzontli&lt;/a&gt;. Each pattern in this series is its own module with a full GoogleTest suite, so you can clone it and run the tests yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Concept
&lt;/h2&gt;

&lt;p&gt;The ring buffer uses two indices, &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt;. To add data we &lt;code&gt;put&lt;/code&gt; a byte at &lt;code&gt;head&lt;/code&gt; and increment &lt;code&gt;head&lt;/code&gt; by one. To remove data we &lt;code&gt;get&lt;/code&gt; a byte from &lt;code&gt;tail&lt;/code&gt; and increment &lt;code&gt;tail&lt;/code&gt; by one. Both indices only move forward, the tail chases the head around the buffer.&lt;/p&gt;

&lt;p&gt;This pattern can be implemented with a linked list or a fixed-length array. A linked list might be tempting, but on an embedded system you don't want dynamic allocation. On a modern computer with gigabytes of memory, calling &lt;code&gt;malloc&lt;/code&gt; is a non-issue. On a microcontroller you have kilobytes, and three bigger problems: the heap fragments over a device that runs for months, allocation timing is non-deterministic, and you often need to add data from inside an interrupt where you can't afford either. A fixed array sidesteps all of it, the space is guaranteed at compile time.&lt;/p&gt;

&lt;p&gt;That guaranteed space is what buys the consumer time: bytes arriving faster than you can process them sit safely in the buffer until you get to them, instead of being lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping around the array
&lt;/h2&gt;

&lt;p&gt;An array does not loop. When you reach the end and try to go past it, it doesn't wrap around. You run beyond the allocated space, which can cause a segmentation fault, return a NULL pointer, or hand you garbage data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intuition
&lt;/h3&gt;

&lt;p&gt;The intuitive way to loop back around is the modulo operator &lt;code&gt;%&lt;/code&gt;, which returns the remainder after division.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Array of length/capacity: 10

Formula: index % capacity -&amp;gt; array index

- index 0: 0 % 10 = 0
- index 1: 1 % 10 = 1
- index 4: 4 % 10 = 4
- index 10: 10 % 10 = 0
- index 11: 11 % 10 = 1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modulo lets us treat the fixed array as a ring, which makes it a great fit for a ring buffer. It works fine on a modern computer with multiple cores, high clock speeds, and a hardware divide unit. But modulo has a drawback. It requires division, and many microcontrollers have no hardware divide. The compiler emulates it in software, which is slow, and we pay that cost on every byte we move through the buffer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimizing with a bitmask
&lt;/h3&gt;

&lt;p&gt;There's a faster way to do exactly what the modulo operator does, with one restriction: the array length must be a power of two. That restriction is the key that lets us recreate modulo with a single bitwise operation. The formula is &lt;code&gt;index = head &amp;amp; mask&lt;/code&gt;, where &lt;code&gt;mask = capacity - 1&lt;/code&gt;. Let's walk through the math.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Array of length/capacity: 8 (power of two)

mask = 8 - 1 = 7 = 0b0111

- index 0: 0b0000 &amp;amp; 0b0111 = 0
- index 1: 0b0001 &amp;amp; 0b0111 = 1
- index 2: 0b0010 &amp;amp; 0b0111 = 2
- index 8: 0b1000 &amp;amp; 0b0111 = 0
- index 10: 0b1010 &amp;amp; 0b0111 = 2
- index 14: 0b1110 &amp;amp; 0b0111 = 6

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice these are the same answers modulo would give: &lt;code&gt;8 % 8 = 0&lt;/code&gt;, &lt;code&gt;10 % 8 = 2&lt;/code&gt;, &lt;code&gt;14 % 8 = 6&lt;/code&gt;. That's not a coincidence. The mask &lt;code&gt;0b0111&lt;/code&gt; keeps only the lowest three bits and clears everything above, and the lowest three bits of any number are exactly that number modulo 8. Keeping the low bits &lt;em&gt;is&lt;/em&gt; taking the remainder. Because the capacity is a power of two, &lt;code&gt;capacity - 1&lt;/code&gt; is a clean run of ones, which is what makes the AND line up perfectly with modulo. Try this with a non power of two and it falls apart: &lt;code&gt;11 &amp;amp; 9 = 9&lt;/code&gt;, but &lt;code&gt;11 % 10 = 1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Ring buffer structure
&lt;/h3&gt;

&lt;p&gt;We create a struct to keep all the data types together. This makes life easy, because with a single pointer to the data structure we can access: &lt;code&gt;buffer&lt;/code&gt;, &lt;code&gt;capacity&lt;/code&gt;, &lt;code&gt;mask&lt;/code&gt;, &lt;code&gt;head&lt;/code&gt;, and &lt;code&gt;tail&lt;/code&gt;. It also makes the data easy to pass by pointer for any function that needs to add and read from it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.h&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see the struct uses a few different types, and each one is deliberate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;uint8_t&lt;/code&gt;: the buffer stores raw bytes. Whatever the source is, serial data from UART, I2C, or anything else, it arrives as octets, so a byte array is the natural storage for the queue.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size_t&lt;/code&gt;: &lt;code&gt;capacity&lt;/code&gt;, &lt;code&gt;mask&lt;/code&gt;, &lt;code&gt;head&lt;/code&gt;, and &lt;code&gt;tail&lt;/code&gt; are all &lt;code&gt;size_t&lt;/code&gt;. It's an unsigned type sized to the platform (usually an alias for &lt;code&gt;unsigned int&lt;/code&gt; or &lt;code&gt;unsigned long&lt;/code&gt;), which keeps the index math portable across compilers and avoids signed-overflow issues. The unsignedness matters later, when we rely on &lt;code&gt;head - tail&lt;/code&gt; wrapping cleanly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;volatile&lt;/code&gt;: &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; are marked &lt;code&gt;volatile&lt;/code&gt; so the compiler always reads them from memory instead of caching a stale copy in a register. The producer and consumer can run in different execution contexts (for example, an ISR filling the buffer while the main loop drains it), so a value cached in a register could miss the other side's update. Note that &lt;code&gt;volatile&lt;/code&gt; only guarantees the read happens, it does not make the access atomic or thread-safe. We'll cover why this design is safe anyway in the section on interrupts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Initialization and power of two functions
&lt;/h3&gt;

&lt;p&gt;Let's start with &lt;code&gt;init&lt;/code&gt; and its helper &lt;code&gt;is_power_of_two&lt;/code&gt;. The &lt;code&gt;init&lt;/code&gt; function sets up the data structure, and the power-of-two check is the first gate: if the caller asks for a capacity that isn't a power of two, we refuse, because the whole masking scheme depends on it. We'll start with the helper.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;is_power_of_two&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1u&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;x != 0u&lt;/code&gt;: checks if &lt;code&gt;x&lt;/code&gt; is not &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(x &amp;amp; (x - 1u)) == 0u&lt;/code&gt;: this is the part that checks for a single set bit. A power of two has exactly one bit set. Subtracting one flips that bit off and turns every bit below it on, so &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;x - 1&lt;/code&gt; share no bits and the AND comes out zero. Take &lt;code&gt;x = 8&lt;/code&gt;: &lt;code&gt;8 - 1 = 7&lt;/code&gt;, and &lt;code&gt;0b1000 &amp;amp; 0b0111 = 0&lt;/code&gt;. Now take a number that isn't a power of two, &lt;code&gt;x = 12&lt;/code&gt;: &lt;code&gt;12 - 1 = 11&lt;/code&gt;, and &lt;code&gt;0b1100 &amp;amp; 0b1011 = 0b1000&lt;/code&gt;, which is not zero. The leftover bit is the tell that &lt;code&gt;x&lt;/code&gt; had more than one bit set. If both conditions are true then &lt;code&gt;x&lt;/code&gt; is a power of two, so the function returns &lt;code&gt;true&lt;/code&gt; if and only if &lt;code&gt;x&lt;/code&gt; is a power of two. Now let's turn to the &lt;code&gt;ring_buffer_init&lt;/code&gt; function.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;is_power_of_two&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;init&lt;/code&gt; validates its inputs first: a null struct pointer, null storage, or a capacity that isn't a power of two all cause it to bail out and return &lt;code&gt;false&lt;/code&gt;. Once the checks pass, it wires up the struct, precomputes &lt;code&gt;mask = capacity - 1&lt;/code&gt; so &lt;code&gt;put&lt;/code&gt; and &lt;code&gt;get&lt;/code&gt; never have to recompute it, and zeroes both indices.&lt;/p&gt;

&lt;p&gt;Notice &lt;code&gt;init&lt;/code&gt; doesn't allocate anything. The caller hands us the &lt;code&gt;storage&lt;/code&gt; array, so the buffer can live in static or stack memory. This is the no-malloc principle from earlier, made concrete: there's no &lt;code&gt;malloc&lt;/code&gt; in sight, and the space was guaranteed the moment the caller declared the array.&lt;/p&gt;

&lt;h3&gt;
  
  
  Put and get functions
&lt;/h3&gt;

&lt;p&gt;These functions put and get a single byte at a time. We scope them to one byte because sometimes a single byte is all we need, and because the next section's &lt;code&gt;write&lt;/code&gt; and &lt;code&gt;read&lt;/code&gt; functions are built on top of them. Let's start with &lt;code&gt;ring_buffer_put&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_is_full&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function starts by validating that &lt;code&gt;rb&lt;/code&gt; isn't &lt;code&gt;NULL&lt;/code&gt;, then calls the helper &lt;code&gt;ring_buffer_is_full&lt;/code&gt;. If the buffer is full we exit early and return &lt;code&gt;false&lt;/code&gt;, telling the caller no byte was stored. We'll see how &lt;code&gt;is_full&lt;/code&gt; works in the next section. If there's room, we do the actual write, and this is where the previous section pays off: &lt;code&gt;rb-&amp;gt;head &amp;amp; rb-&amp;gt;mask&lt;/code&gt; computes the array index using the bitwise AND, wrapping the free-running &lt;code&gt;head&lt;/code&gt; back into a valid slot. We write the byte there, then increment &lt;code&gt;head&lt;/code&gt; by one so the next &lt;code&gt;put&lt;/code&gt; lands in the following slot. Notice the order: we write the slot first, then advance &lt;code&gt;head&lt;/code&gt;. That ordering matters for interrupt safety, which we'll get to later. Now let's take a look at &lt;code&gt;ring_buffer_get&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_is_empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function also starts by validating that &lt;code&gt;rb&lt;/code&gt; isn't &lt;code&gt;NULL&lt;/code&gt;, then checks that &lt;code&gt;out&lt;/code&gt; isn't &lt;code&gt;NULL&lt;/code&gt;. This is the pointer where the byte we read gets stored, which is how &lt;code&gt;get&lt;/code&gt; hands data back to the caller while keeping its &lt;code&gt;bool&lt;/code&gt; return free to report success or failure. Finally it calls the helper &lt;code&gt;ring_buffer_is_empty&lt;/code&gt;. If the buffer is empty we exit early and return &lt;code&gt;false&lt;/code&gt;, telling the caller no byte was read. We'll see how &lt;code&gt;is_empty&lt;/code&gt; works in the next section. If it's not empty, we do the read, and again the previous section pays off: &lt;code&gt;rb-&amp;gt;tail &amp;amp; rb-&amp;gt;mask&lt;/code&gt; computes the array index with the bitwise AND, wrapping the free-running &lt;code&gt;tail&lt;/code&gt; into a valid slot. We read the byte, then increment &lt;code&gt;tail&lt;/code&gt; by one. Notice &lt;code&gt;get&lt;/code&gt; never erases anything. It reads the byte and moves &lt;code&gt;tail&lt;/code&gt; forward, and that slot counts as free again the moment &lt;code&gt;tail&lt;/code&gt; passes it. The indices alone decide what's data and what's free space.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is empty, is full, and count
&lt;/h3&gt;

&lt;p&gt;These three helpers, &lt;code&gt;is_empty&lt;/code&gt;, &lt;code&gt;is_full&lt;/code&gt;, and &lt;code&gt;count&lt;/code&gt;, are what &lt;code&gt;put&lt;/code&gt; and &lt;code&gt;get&lt;/code&gt; lean on to decide whether they can proceed. This is where the free-running indices finally earn their keep. Let's start with &lt;code&gt;ring_buffer_is_empty&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_is_empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The buffer is empty when &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; hold the same value. That makes sense: &lt;code&gt;tail&lt;/code&gt; chases &lt;code&gt;head&lt;/code&gt;, and when it catches all the way up there's nothing left to read. Here's the subtle part. In a lot of ring buffer designs the indices get wrapped back into range, and then &lt;code&gt;head == tail&lt;/code&gt; is ambiguous, it means &lt;em&gt;both&lt;/em&gt; completely empty and completely full, and you can't tell which. Because our indices run free and never wrap, &lt;code&gt;head == tail&lt;/code&gt; can only ever mean empty. We'll see in a moment how full is handled without that ambiguity.&lt;/p&gt;

&lt;p&gt;Next, &lt;code&gt;ring_buffer_count&lt;/code&gt;, which is the heart of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both indices only ever increase. Every &lt;code&gt;put&lt;/code&gt; bumps &lt;code&gt;head&lt;/code&gt;, every &lt;code&gt;get&lt;/code&gt; bumps &lt;code&gt;tail&lt;/code&gt;, and neither wraps. So &lt;code&gt;head&lt;/code&gt; is always ahead of &lt;code&gt;tail&lt;/code&gt; by exactly the number of bytes that have been written but not yet read, which is the fill level. The difference &lt;code&gt;head - tail&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; the count.&lt;/p&gt;

&lt;p&gt;The nice part is what happens when &lt;code&gt;head&lt;/code&gt; eventually overflows. &lt;code&gt;size_t&lt;/code&gt; has a maximum, and after enough bytes &lt;code&gt;head&lt;/code&gt; wraps past it back toward zero. You might expect &lt;code&gt;head - tail&lt;/code&gt; to break at that point, but it doesn't. Unsigned subtraction wraps the same way unsigned addition does, so the difference stays correct even across the overflow. This is exactly why &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; are unsigned, and it's why the buffer can run indefinitely without any special handling for the wraparound.&lt;/p&gt;

&lt;p&gt;Finally, &lt;code&gt;ring_buffer_is_full&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_is_full&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have &lt;code&gt;count&lt;/code&gt;, full is trivial: the buffer is full when it holds &lt;code&gt;capacity&lt;/code&gt; bytes. No wasted slot, no separate counter to keep in sync, no ambiguity with the empty case. The free-running indices give us empty (&lt;code&gt;head == tail&lt;/code&gt;), full (&lt;code&gt;count == capacity&lt;/code&gt;), and everything in between, all from two numbers that only count upward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write and read functions
&lt;/h3&gt;

&lt;p&gt;These functions write and read more than a single byte of data at a time. Let's start with &lt;code&gt;ring_buffer_write&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;written&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;written&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;written&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;written&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;written&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function returns the number of bytes actually written, which may be fewer than requested. It returns &lt;code&gt;0&lt;/code&gt; on the early exit when either the ring buffer &lt;code&gt;rb&lt;/code&gt; or the source pointer &lt;code&gt;data&lt;/code&gt; is &lt;code&gt;NULL&lt;/code&gt;. Otherwise we initialize &lt;code&gt;written&lt;/code&gt; to track how many bytes we've copied from &lt;code&gt;data&lt;/code&gt; into the buffer. The loop runs on two conditions joined by &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;: &lt;code&gt;written &amp;lt; len&lt;/code&gt; keeps us from reading past the end of the input, and &lt;code&gt;ring_buffer_put&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt; the moment the buffer is full. As long as both hold, we keep copying bytes and incrementing &lt;code&gt;written&lt;/code&gt;. The loop stops as soon as either one fails, whether we ran out of input or ran out of room. The count we return tells the caller exactly how much made it in: all of it, part of it, or zero if the buffer was already full.&lt;/p&gt;

&lt;p&gt;Next, &lt;code&gt;ring_buffer_read&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function also returns the number of bytes actually read, which may be fewer than requested. It returns &lt;code&gt;0&lt;/code&gt; on the early exit when either the ring buffer &lt;code&gt;rb&lt;/code&gt; or the destination pointer &lt;code&gt;out&lt;/code&gt; is &lt;code&gt;NULL&lt;/code&gt;. Otherwise we initialize &lt;code&gt;count&lt;/code&gt; to track how many bytes we've read from the buffer into &lt;code&gt;out&lt;/code&gt;. The loop runs on two conditions joined by &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;: &lt;code&gt;count &amp;lt; len&lt;/code&gt; keeps us from writing past the end of the output, and &lt;code&gt;ring_buffer_get&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt; the moment the buffer is empty. As long as both hold, we keep reading bytes and incrementing &lt;code&gt;count&lt;/code&gt;. The loop stops as soon as either one fails, whether the caller's buffer is full or the ring buffer ran dry. The count we return tells the caller exactly how many bytes they got.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety with the Interrupt Service Routine (ISR)
&lt;/h2&gt;

&lt;p&gt;For those new to embedded, it might not be obvious why this design is considered safe, or what its typical applications are. The primary use for a ring buffer, as we mentioned earlier, is serial communication. Take the example of two microcontrollers talking to each other, each running its own firmware tailored to its own task. We don't know when either one will need to send data, so to make sure we don't miss a single byte, we set up the data receive (RX) path using the microcontroller's Interrupt Service Routine (ISR). As soon as the start of a transmission arrives, the ISR takes over and captures each byte.&lt;/p&gt;

&lt;p&gt;This is the producer side of the buffer. The ISR calls &lt;code&gt;put&lt;/code&gt; for every byte it receives. Somewhere else, the main loop is the consumer, calling &lt;code&gt;get&lt;/code&gt; when it gets around to processing what came in. The two run in different execution contexts, and here's the catch: the ISR can fire at any moment, between any two instructions the main loop is running. So the fair question is, if the interrupt lands in the middle of a &lt;code&gt;get&lt;/code&gt;, can the two step on each other and corrupt the buffer?&lt;/p&gt;

&lt;p&gt;The answer is no, and the reason is that each index has exactly one writer. &lt;code&gt;put&lt;/code&gt; is the only function that writes &lt;code&gt;head&lt;/code&gt;, and it only ever runs in the ISR. &lt;code&gt;get&lt;/code&gt; is the only function that writes &lt;code&gt;tail&lt;/code&gt;, and it only ever runs in the main loop. Neither side writes the other's index. The producer reads &lt;code&gt;tail&lt;/code&gt; to check whether the buffer is full, and the consumer reads &lt;code&gt;head&lt;/code&gt; to check whether it's empty, but reading is safe. Since no index has two writers racing to change it, there's nothing to corrupt, and we don't need to disable interrupts or reach for a lock. This is what people mean by single-producer, single-consumer, or SPSC.&lt;/p&gt;

&lt;p&gt;That single-writer rule is also the boundary. The moment you have two things calling &lt;code&gt;put&lt;/code&gt;, or two things calling &lt;code&gt;get&lt;/code&gt;, the guarantee is gone and you need real synchronization. This buffer is safe for exactly one producer and one consumer, no more.&lt;/p&gt;

&lt;p&gt;One more detail makes it work, and it's the ordering we flagged back in &lt;code&gt;put&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 1. write the data&lt;/span&gt;
&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 2. then publish it&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We write the byte into the slot first, then increment &lt;code&gt;head&lt;/code&gt;. That order is not an accident. &lt;code&gt;head&lt;/code&gt; is the signal that tells the consumer a new byte is available. If we bumped &lt;code&gt;head&lt;/code&gt; first and the interrupt timing worked out badly, the consumer could see the new &lt;code&gt;head&lt;/code&gt;, go read that slot, and pull out a byte we hadn't actually written yet. By writing the slot before advancing &lt;code&gt;head&lt;/code&gt;, we guarantee that the instant the consumer can see the byte, the byte is really there. The index update publishes the data, so it comes last. &lt;code&gt;get&lt;/code&gt; mirrors this: we read the byte out first, then advance &lt;code&gt;tail&lt;/code&gt;, so we never mark a slot free before we've taken its contents.&lt;/p&gt;

&lt;p&gt;A couple of honest caveats for the readers who want them. First, &lt;code&gt;volatile&lt;/code&gt; gets us visibility, not atomicity. This design assumes reading or writing &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; happens in a single, uninterruptible machine operation, which is true when they're word-sized on the target. On an 8-bit MCU where &lt;code&gt;size_t&lt;/code&gt; spans multiple bytes, an index update could be split across instructions and torn by an interrupt, and you'd need to account for that. Second, on a single-core microcontroller with an ISR, &lt;code&gt;volatile&lt;/code&gt; plus the write-then-publish ordering is enough. On a multi-core system with weak memory ordering you'd reach for atomics with explicit acquire and release semantics to guarantee the slot write is seen before the index write. For the classic ISR-and-main-loop case this buffer is built for, we're on solid ground.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;That's a complete, working ring buffer in a couple hundred lines of C. It does no dynamic allocation, wraps with a single bitwise AND instead of a division, tells full from empty without wasting a slot or keeping a separate counter, and it's safe to fill from an interrupt while draining it from the main loop. For a structure this small, there's a surprising amount of nuance packed in.&lt;/p&gt;

&lt;p&gt;It's worth being honest about what this design does not do, because every one of these is a deliberate trade, not an oversight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Capacity must be a power of two.&lt;/strong&gt; This is the price of replacing modulo with a mask. If you need to hold exactly 100 bytes you round up to 128 and waste a little space. On an embedded system that's almost always a trade worth making.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single producer, single consumer only.&lt;/strong&gt; One writer per index is what makes it lock-free. Two ISRs writing, or two contexts reading, and you need real synchronization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No overwrite-oldest mode.&lt;/strong&gt; When the buffer is full, &lt;code&gt;put&lt;/code&gt; refuses the new byte rather than discarding the oldest one. Some applications want the opposite (keep the newest samples, drop the stale ones). That's a reasonable variant, just not this one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bytes only.&lt;/strong&gt; This buffer stores &lt;code&gt;uint8_t&lt;/code&gt;. Storing wider elements, or arbitrary structs, means generalizing the element size, which changes the index math and the storage. None of these are hard to lift. They're the natural next questions, and a few of them turn into their own patterns later in this series.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Speaking of which, the ring buffer is a foundation, not a destination. It shows up underneath several of the patterns still to come: an event queue is a ring buffer of messages instead of bytes, a moving-average filter is a ring buffer of samples, and the command dispatcher we'll build reads its input straight out of one. Next in the series we'll tackle the memory pool. The ring buffer gave us fixed storage for bytes; the memory pool gives us fixed storage for objects, and it's what lets an event queue hold structs instead of raw data. See you there.&lt;/p&gt;

</description>
      <category>embeddedsystems</category>
      <category>datastructures</category>
      <category>c</category>
      <category>microcontrollers</category>
    </item>
    <item>
      <title>Patrones embebidos comunes: búfer circular</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/patrones-embebidos-comunes-bufer-circular-3280</link>
      <guid>https://dev.to/elcapitan/patrones-embebidos-comunes-bufer-circular-3280</guid>
      <description>&lt;p&gt;Esta es la primera parte de una nueva serie donde vuelvo a explorar los patrones embebidos más comunes, empezando por el búfer circular (ring buffer). Es uno de esos patrones que se usan en la comunicación serial para asegurarnos de no perder ni una trama de datos, y además es un bloque de construcción para otros patrones que veremos más adelante.&lt;/p&gt;

&lt;p&gt;El código fuente completo y probado vive en el repositorio complementario: &lt;a href="https://github.com/ileanmjr88/tetzontli" rel="noopener noreferrer"&gt;ileanmjr88/tetzontli&lt;/a&gt;. Cada patrón de esta serie es su propio módulo con una suite completa de pruebas en GoogleTest, así que puedes clonarlo y ejecutar las pruebas tú mismo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concepto básico
&lt;/h2&gt;

&lt;p&gt;El búfer circular usa dos índices, &lt;code&gt;head&lt;/code&gt; y &lt;code&gt;tail&lt;/code&gt;. Para agregar datos hacemos &lt;code&gt;put&lt;/code&gt; de un byte en &lt;code&gt;head&lt;/code&gt; e incrementamos &lt;code&gt;head&lt;/code&gt; en uno. Para quitar datos hacemos &lt;code&gt;get&lt;/code&gt; de un byte en &lt;code&gt;tail&lt;/code&gt; e incrementamos &lt;code&gt;tail&lt;/code&gt; en uno. Ambos índices solo avanzan hacia adelante: &lt;code&gt;tail&lt;/code&gt; persigue a &lt;code&gt;head&lt;/code&gt; por todo el búfer.&lt;/p&gt;

&lt;p&gt;Este patrón se puede implementar con una lista enlazada o con un arreglo (array) de longitud fija. La lista enlazada suena tentadora, pero en un sistema embebido no quieres asignación dinámica de memoria. En una computadora moderna con gigabytes de memoria, llamar a &lt;code&gt;malloc&lt;/code&gt; no es problema. En un microcontrolador tienes kilobytes, y tres problemas más grandes: el heap se fragmenta en un dispositivo que funciona durante meses, el tiempo que tarda una asignación no es determinista, y muchas veces necesitas agregar datos desde dentro de una interrupción, donde no te puedes dar el lujo de ninguna de las dos cosas. Un arreglo fijo evita todo eso, porque el espacio queda garantizado en tiempo de compilación.&lt;/p&gt;

&lt;p&gt;Ese espacio garantizado es justo lo que le da tiempo al consumidor: los bytes que llegan más rápido de lo que los puedes procesar se quedan seguros en el búfer hasta que llegues a ellos, en lugar de perderse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dar la vuelta dentro del arreglo
&lt;/h2&gt;

&lt;p&gt;Un arreglo no da la vuelta. Cuando llegas al final e intentas pasarte, no regresa al inicio. Te sales del espacio asignado, lo que puede provocar un fallo de segmentación, devolverte un puntero NULL o entregarte basura.&lt;/p&gt;

&lt;h3&gt;
  
  
  La intuición
&lt;/h3&gt;

&lt;p&gt;La forma intuitiva de regresar al inicio es el operador módulo &lt;code&gt;%&lt;/code&gt;, que devuelve el residuo de una división.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Array of length/capacity: 10

Formula: index % capacity -&amp;gt; array index

- index 0: 0 % 10 = 0
- index 1: 1 % 10 = 1
- index 4: 4 % 10 = 4
- index 10: 10 % 10 = 0
- index 11: 11 % 10 = 1

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El módulo nos deja tratar el arreglo fijo como un anillo, lo que lo hace ideal para un búfer circular. Funciona bien en una computadora moderna con varios núcleos, frecuencias altas y una unidad de división por hardware. Pero el módulo tiene una desventaja: requiere división, y muchos microcontroladores no tienen división por hardware. El compilador la emula por software, que es lento, y pagamos ese costo con cada byte que pasa por el búfer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimizando con una máscara de bits
&lt;/h3&gt;

&lt;p&gt;Hay una forma más rápida de hacer exactamente lo mismo que el operador módulo, con una restricción: la longitud del arreglo debe ser una potencia de dos. Esa restricción es la llave que nos permite recrear el módulo con una sola operación a nivel de bits. La fórmula es &lt;code&gt;index = head &amp;amp; mask&lt;/code&gt;, donde &lt;code&gt;mask = capacity - 1&lt;/code&gt;. Repasemos las matemáticas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Array of length/capacity: 8 (power of two)

mask = 8 - 1 = 7 = 0b0111

- index 0: 0b0000 &amp;amp; 0b0111 = 0
- index 1: 0b0001 &amp;amp; 0b0111 = 1
- index 2: 0b0010 &amp;amp; 0b0111 = 2
- index 8: 0b1000 &amp;amp; 0b0111 = 0
- index 10: 0b1010 &amp;amp; 0b0111 = 2
- index 14: 0b1110 &amp;amp; 0b0111 = 6

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fíjate que son las mismas respuestas que daría el módulo: &lt;code&gt;8 % 8 = 0&lt;/code&gt;, &lt;code&gt;10 % 8 = 2&lt;/code&gt;, &lt;code&gt;14 % 8 = 6&lt;/code&gt;. No es coincidencia. La máscara &lt;code&gt;0b0111&lt;/code&gt; conserva solo los tres bits más bajos y limpia todo lo que está arriba, y los tres bits más bajos de cualquier número son exactamente ese número módulo 8. Quedarte con los bits bajos &lt;em&gt;es&lt;/em&gt; sacar el residuo. Como la capacidad es potencia de dos, &lt;code&gt;capacity - 1&lt;/code&gt; es una secuencia limpia de unos, y eso es lo que hace que el AND coincida perfectamente con el módulo. Intenta esto con un número que no sea potencia de dos y se cae todo: &lt;code&gt;11 &amp;amp; 9 = 9&lt;/code&gt;, pero &lt;code&gt;11 % 10 = 1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementación
&lt;/h2&gt;

&lt;h3&gt;
  
  
  La estructura del búfer circular
&lt;/h3&gt;

&lt;p&gt;Creamos un struct para mantener juntos todos los datos. Esto nos facilita la vida, porque con un solo puntero a la estructura podemos acceder a &lt;code&gt;buffer&lt;/code&gt;, &lt;code&gt;capacity&lt;/code&gt;, &lt;code&gt;mask&lt;/code&gt;, &lt;code&gt;head&lt;/code&gt; y &lt;code&gt;tail&lt;/code&gt;. También hace que los datos sean fáciles de pasar por puntero a cualquier función que necesite agregar o leer de él.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.h&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como puedes ver, el struct usa varios tipos distintos, y cada uno es deliberado.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;uint8_t&lt;/code&gt;: el búfer guarda bytes crudos. Sea cual sea la fuente, datos seriales de UART, I2C o cualquier otra cosa, llegan como octetos, así que un arreglo de bytes es el almacenamiento natural para la cola.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;size_t&lt;/code&gt;: &lt;code&gt;capacity&lt;/code&gt;, &lt;code&gt;mask&lt;/code&gt;, &lt;code&gt;head&lt;/code&gt; y &lt;code&gt;tail&lt;/code&gt; son todos &lt;code&gt;size_t&lt;/code&gt;. Es un tipo sin signo del tamaño de la plataforma (normalmente un alias de &lt;code&gt;unsigned int&lt;/code&gt; o &lt;code&gt;unsigned long&lt;/code&gt;), lo que mantiene la aritmética de índices portable entre compiladores y evita problemas de desbordamiento con signo. Que sea sin signo importa más adelante, cuando dependemos de que &lt;code&gt;head - tail&lt;/code&gt; dé la vuelta limpiamente.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;volatile&lt;/code&gt;: &lt;code&gt;head&lt;/code&gt; y &lt;code&gt;tail&lt;/code&gt; están marcados como &lt;code&gt;volatile&lt;/code&gt; para que el compilador siempre los lea de memoria en lugar de guardarse una copia vieja en un registro. El productor y el consumidor pueden ejecutarse en contextos distintos (por ejemplo, una ISR llenando el búfer mientras el bucle principal lo vacía), así que un valor guardado en un registro podría perderse la actualización del otro lado. Ojo: &lt;code&gt;volatile&lt;/code&gt; solo garantiza que la lectura ocurra, no hace que el acceso sea atómico ni seguro entre hilos. Veremos por qué este diseño es seguro de todos modos en la sección sobre interrupciones.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Las funciones de inicialización y de potencia de dos
&lt;/h3&gt;

&lt;p&gt;Empecemos con &lt;code&gt;init&lt;/code&gt; y su ayudante &lt;code&gt;is_power_of_two&lt;/code&gt;. La función &lt;code&gt;init&lt;/code&gt; prepara la estructura de datos, y la verificación de potencia de dos es el primer filtro: si quien la llama pide una capacidad que no es potencia de dos, la rechazamos, porque todo el esquema de enmascarado depende de eso. Arranquemos con el ayudante.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;is_power_of_two&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1u&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta implementación hace lo siguiente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;x != 0u&lt;/code&gt;: verifica que &lt;code&gt;x&lt;/code&gt; no sea &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(x &amp;amp; (x - 1u)) == 0u&lt;/code&gt;: esta es la parte que verifica que haya un solo bit encendido. Una potencia de dos tiene exactamente un bit encendido. Restarle uno apaga ese bit y enciende todos los que están debajo, así que &lt;code&gt;x&lt;/code&gt; y &lt;code&gt;x - 1&lt;/code&gt; no comparten ningún bit y el AND da cero. Toma &lt;code&gt;x = 8&lt;/code&gt;: &lt;code&gt;8 - 1 = 7&lt;/code&gt;, y &lt;code&gt;0b1000 &amp;amp; 0b0111 = 0&lt;/code&gt;. Ahora toma un número que no sea potencia de dos, &lt;code&gt;x = 12&lt;/code&gt;: &lt;code&gt;12 - 1 = 11&lt;/code&gt;, y &lt;code&gt;0b1100 &amp;amp; 0b1011 = 0b1000&lt;/code&gt;, que no es cero. Ese bit que sobra delata que &lt;code&gt;x&lt;/code&gt; tenía más de un bit encendido.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si ambas condiciones se cumplen, entonces &lt;code&gt;x&lt;/code&gt; es potencia de dos, así que la función devuelve &lt;code&gt;true&lt;/code&gt; si y solo si &lt;code&gt;x&lt;/code&gt; es potencia de dos. Ahora pasemos a la función &lt;code&gt;ring_buffer_init&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;is_power_of_two&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;init&lt;/code&gt; valida primero sus entradas: un puntero nulo al struct, un &lt;code&gt;storage&lt;/code&gt; nulo, o una capacidad que no sea potencia de dos hacen que se salga y devuelva &lt;code&gt;false&lt;/code&gt;. Una vez que pasan las verificaciones, arma el struct, precalcula &lt;code&gt;mask = capacity - 1&lt;/code&gt; para que &lt;code&gt;put&lt;/code&gt; y &lt;code&gt;get&lt;/code&gt; nunca tengan que recalcularlo, y pone ambos índices en cero.&lt;/p&gt;

&lt;p&gt;Fíjate que &lt;code&gt;init&lt;/code&gt; no asigna nada. Quien la llama nos entrega el arreglo &lt;code&gt;storage&lt;/code&gt;, así que el búfer puede vivir en memoria estática o en la pila. Este es el principio de no usar &lt;code&gt;malloc&lt;/code&gt; que mencionamos antes, hecho concreto: no hay ni un &lt;code&gt;malloc&lt;/code&gt; a la vista, y el espacio quedó garantizado en el momento en que el llamador declaró el arreglo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Las funciones put y get
&lt;/h3&gt;

&lt;p&gt;Estas funciones ponen y sacan un solo byte a la vez. Las limitamos a un byte porque a veces un solo byte es todo lo que necesitamos, y porque las funciones &lt;code&gt;write&lt;/code&gt; y &lt;code&gt;read&lt;/code&gt; de la siguiente sección están construidas encima de ellas. Empecemos con &lt;code&gt;ring_buffer_put&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_is_full&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La función empieza validando que &lt;code&gt;rb&lt;/code&gt; no sea &lt;code&gt;NULL&lt;/code&gt;, y luego llama al ayudante &lt;code&gt;ring_buffer_is_full&lt;/code&gt;. Si el búfer está lleno salimos temprano y devolvemos &lt;code&gt;false&lt;/code&gt;, avisándole al llamador que no se guardó ningún byte. Veremos cómo funciona &lt;code&gt;is_full&lt;/code&gt; en la siguiente sección. Si hay espacio, hacemos la escritura, y aquí es donde rinde frutos la sección anterior: &lt;code&gt;rb-&amp;gt;head &amp;amp; rb-&amp;gt;mask&lt;/code&gt; calcula el índice del arreglo con el AND a nivel de bits, envolviendo el &lt;code&gt;head&lt;/code&gt; de avance libre de regreso a una ranura válida. Escribimos el byte ahí, y luego incrementamos &lt;code&gt;head&lt;/code&gt; en uno para que el siguiente &lt;code&gt;put&lt;/code&gt; caiga en la ranura que sigue. Fíjate en el orden: primero escribimos la ranura, después avanzamos &lt;code&gt;head&lt;/code&gt;. Ese orden importa para la seguridad con interrupciones, y llegaremos a eso más adelante. Ahora veamos &lt;code&gt;ring_buffer_get&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_is_empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta función también empieza validando que &lt;code&gt;rb&lt;/code&gt; no sea &lt;code&gt;NULL&lt;/code&gt;, y luego verifica que &lt;code&gt;out&lt;/code&gt; tampoco lo sea. Ese es el puntero donde se guarda el byte que leemos, que es la forma en que &lt;code&gt;get&lt;/code&gt; le devuelve datos al llamador dejando libre su valor de retorno &lt;code&gt;bool&lt;/code&gt; para reportar éxito o fracaso. Por último llama al ayudante &lt;code&gt;ring_buffer_is_empty&lt;/code&gt;. Si el búfer está vacío salimos temprano y devolvemos &lt;code&gt;false&lt;/code&gt;, avisándole al llamador que no se leyó ningún byte. Veremos cómo funciona &lt;code&gt;is_empty&lt;/code&gt; en la siguiente sección. Si no está vacío, hacemos la lectura, y otra vez la sección anterior rinde frutos: &lt;code&gt;rb-&amp;gt;tail &amp;amp; rb-&amp;gt;mask&lt;/code&gt; calcula el índice del arreglo con el AND a nivel de bits, envolviendo el &lt;code&gt;tail&lt;/code&gt; de avance libre en una ranura válida. Leemos el byte e incrementamos &lt;code&gt;tail&lt;/code&gt; en uno. Fíjate que &lt;code&gt;get&lt;/code&gt; nunca borra nada. Lee el byte y mueve &lt;code&gt;tail&lt;/code&gt; hacia adelante, y esa ranura cuenta como libre en el momento en que &lt;code&gt;tail&lt;/code&gt; pasa por encima de ella. Los índices por sí solos deciden qué son datos y qué es espacio libre.&lt;/p&gt;

&lt;h3&gt;
  
  
  Las funciones is_empty, is_full y count
&lt;/h3&gt;

&lt;p&gt;Estos tres ayudantes, &lt;code&gt;is_empty&lt;/code&gt;, &lt;code&gt;is_full&lt;/code&gt; y &lt;code&gt;count&lt;/code&gt;, son en los que se apoyan &lt;code&gt;put&lt;/code&gt; y &lt;code&gt;get&lt;/code&gt; para decidir si pueden seguir adelante. Aquí es donde los índices de avance libre por fin se ganan su lugar. Empecemos con &lt;code&gt;ring_buffer_is_empty&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_is_empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El búfer está vacío cuando &lt;code&gt;head&lt;/code&gt; y &lt;code&gt;tail&lt;/code&gt; tienen el mismo valor. Tiene sentido: &lt;code&gt;tail&lt;/code&gt; persigue a &lt;code&gt;head&lt;/code&gt;, y cuando lo alcanza por completo ya no queda nada por leer. Aquí está la parte sutil. En muchos diseños de búfer circular los índices se envuelven de regreso al rango válido, y entonces &lt;code&gt;head == tail&lt;/code&gt; es ambiguo, porque significa &lt;em&gt;tanto&lt;/em&gt; completamente vacío &lt;em&gt;como&lt;/em&gt; completamente lleno, y no puedes distinguir cuál. Como nuestros índices avanzan libres y nunca se envuelven, &lt;code&gt;head == tail&lt;/code&gt; solo puede significar vacío. En un momento veremos cómo se maneja el caso lleno sin esa ambigüedad.&lt;/p&gt;

&lt;p&gt;Sigue &lt;code&gt;ring_buffer_count&lt;/code&gt;, que es el corazón de todo esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ambos índices solo aumentan. Cada &lt;code&gt;put&lt;/code&gt; incrementa &lt;code&gt;head&lt;/code&gt;, cada &lt;code&gt;get&lt;/code&gt; incrementa &lt;code&gt;tail&lt;/code&gt;, y ninguno se envuelve. Así que &lt;code&gt;head&lt;/code&gt; siempre va adelante de &lt;code&gt;tail&lt;/code&gt; por exactamente la cantidad de bytes que se han escrito pero todavía no se han leído, que es el nivel de llenado. La diferencia &lt;code&gt;head - tail&lt;/code&gt; &lt;em&gt;es&lt;/em&gt; la cuenta.&lt;/p&gt;

&lt;p&gt;Lo bonito es lo que pasa cuando &lt;code&gt;head&lt;/code&gt; finalmente se desborda. &lt;code&gt;size_t&lt;/code&gt; tiene un máximo, y después de suficientes bytes &lt;code&gt;head&lt;/code&gt; lo rebasa y da la vuelta hacia cero. Podrías esperar que &lt;code&gt;head - tail&lt;/code&gt; se rompa en ese punto, pero no pasa. La resta sin signo da la vuelta igual que la suma sin signo, así que la diferencia sigue siendo correcta incluso a través del desbordamiento. Justo por esto &lt;code&gt;head&lt;/code&gt; y &lt;code&gt;tail&lt;/code&gt; son sin signo, y por esto el búfer puede funcionar indefinidamente sin ningún manejo especial para el desbordamiento.&lt;/p&gt;

&lt;p&gt;Por último, &lt;code&gt;ring_buffer_is_full&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_is_full&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez que tenemos &lt;code&gt;count&lt;/code&gt;, lo de lleno es trivial: el búfer está lleno cuando contiene &lt;code&gt;capacity&lt;/code&gt; bytes. Sin desperdiciar una ranura, sin un contador aparte que mantener sincronizado, sin ambigüedad con el caso vacío. Los índices de avance libre nos dan vacío (&lt;code&gt;head == tail&lt;/code&gt;), lleno (&lt;code&gt;count == capacity&lt;/code&gt;) y todo lo que hay en medio, todo a partir de dos números que solo cuentan hacia arriba.&lt;/p&gt;

&lt;h3&gt;
  
  
  Las funciones write y read
&lt;/h3&gt;

&lt;p&gt;Estas funciones escriben y leen más de un byte a la vez. Empecemos con &lt;code&gt;ring_buffer_write&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;written&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;written&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;written&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;written&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;written&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta función devuelve la cantidad de bytes que realmente se escribieron, que puede ser menor a la que se pidió. Devuelve &lt;code&gt;0&lt;/code&gt; en la salida temprana, cuando el búfer circular &lt;code&gt;rb&lt;/code&gt; o el puntero de origen &lt;code&gt;data&lt;/code&gt; son &lt;code&gt;NULL&lt;/code&gt;. Si no, inicializamos &lt;code&gt;written&lt;/code&gt; para llevar la cuenta de cuántos bytes hemos copiado de &lt;code&gt;data&lt;/code&gt; al búfer. El ciclo se ejecuta con dos condiciones unidas por &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;: &lt;code&gt;written &amp;lt; len&lt;/code&gt; nos impide leer más allá del final de la entrada, y &lt;code&gt;ring_buffer_put&lt;/code&gt; devuelve &lt;code&gt;false&lt;/code&gt; en cuanto el búfer se llena. Mientras ambas se cumplan, seguimos copiando bytes e incrementando &lt;code&gt;written&lt;/code&gt;. El ciclo se detiene en cuanto una de las dos falla, ya sea porque se nos acabó la entrada o porque se nos acabó el espacio. La cuenta que devolvemos le dice al llamador exactamente cuánto entró: todo, una parte, o cero si el búfer ya estaba lleno.&lt;/p&gt;

&lt;p&gt;Sigue &lt;code&gt;ring_buffer_read&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// modules/ring_buffer/ring_buffer.c&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;ring_buffer_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ring_buffer_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ring_buffer_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta función también devuelve la cantidad de bytes que realmente se leyeron, que puede ser menor a la que se pidió. Devuelve &lt;code&gt;0&lt;/code&gt; en la salida temprana, cuando el búfer circular &lt;code&gt;rb&lt;/code&gt; o el puntero de destino &lt;code&gt;out&lt;/code&gt; son &lt;code&gt;NULL&lt;/code&gt;. Si no, inicializamos &lt;code&gt;count&lt;/code&gt; para llevar la cuenta de cuántos bytes hemos leído del búfer hacia &lt;code&gt;out&lt;/code&gt;. El ciclo se ejecuta con dos condiciones unidas por &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;: &lt;code&gt;count &amp;lt; len&lt;/code&gt; nos impide escribir más allá del final de la salida, y &lt;code&gt;ring_buffer_get&lt;/code&gt; devuelve &lt;code&gt;false&lt;/code&gt; en cuanto el búfer se vacía. Mientras ambas se cumplan, seguimos leyendo bytes e incrementando &lt;code&gt;count&lt;/code&gt;. El ciclo se detiene en cuanto una de las dos falla, ya sea porque el búfer del llamador se llenó o porque el búfer circular se quedó seco. La cuenta que devolvemos le dice al llamador exactamente cuántos bytes recibió.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seguridad con la rutina de servicio de interrupción (ISR)
&lt;/h2&gt;

&lt;p&gt;Para quienes van empezando en embebidos, puede que no sea obvio por qué se considera seguro este diseño, ni cuáles son sus aplicaciones típicas. El uso principal de un búfer circular, como mencionamos antes, es la comunicación serial. Toma el ejemplo de dos microcontroladores hablando entre sí, cada uno ejecutando su propio firmware hecho a la medida de su tarea. No sabemos cuándo va a necesitar enviar datos ninguno de los dos, así que para asegurarnos de no perder un solo byte, montamos la ruta de recepción de datos (RX) usando la rutina de servicio de interrupción (Interrupt Service Routine, ISR) del microcontrolador. En cuanto llega el inicio de una transmisión, la ISR toma el control y captura cada byte.&lt;/p&gt;

&lt;p&gt;Este es el lado productor del búfer. La ISR llama a &lt;code&gt;put&lt;/code&gt; por cada byte que recibe. En otro lado, el bucle principal es el consumidor, llamando a &lt;code&gt;get&lt;/code&gt; cuando tiene chance de procesar lo que llegó. Los dos se ejecutan en contextos distintos, y aquí está el detalle: la ISR puede dispararse en cualquier momento, entre cualesquiera dos instrucciones que esté ejecutando el bucle principal. Así que la pregunta justa es: si la interrupción cae a la mitad de un &lt;code&gt;get&lt;/code&gt;, ¿pueden pisarse entre sí y corromper el búfer?&lt;/p&gt;

&lt;p&gt;La respuesta es no, y la razón es que cada índice tiene exactamente un escritor. &lt;code&gt;put&lt;/code&gt; es la única función que escribe &lt;code&gt;head&lt;/code&gt;, y solo se ejecuta en la ISR. &lt;code&gt;get&lt;/code&gt; es la única función que escribe &lt;code&gt;tail&lt;/code&gt;, y solo se ejecuta en el bucle principal. Ninguno de los dos lados escribe el índice del otro. El productor lee &lt;code&gt;tail&lt;/code&gt; para saber si el búfer está lleno, y el consumidor lee &lt;code&gt;head&lt;/code&gt; para saber si está vacío, pero leer es seguro. Como ningún índice tiene dos escritores compitiendo por cambiarlo, no hay nada que corromper, y no necesitamos deshabilitar interrupciones ni recurrir a un candado. A esto se refiere la gente cuando habla de un solo productor y un solo consumidor, o SPSC por sus siglas en inglés.&lt;/p&gt;

&lt;p&gt;Esa regla de un solo escritor también es el límite. En el momento en que tengas dos cosas llamando a &lt;code&gt;put&lt;/code&gt;, o dos cosas llamando a &lt;code&gt;get&lt;/code&gt;, la garantía se acaba y necesitas sincronización de verdad. Este búfer es seguro para exactamente un productor y un consumidor, ni uno más.&lt;/p&gt;

&lt;p&gt;Hay un detalle más que lo hace funcionar, y es el orden que señalamos allá en &lt;code&gt;put&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 1. write the data&lt;/span&gt;
&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 2. then publish it&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Primero escribimos el byte en la ranura, y después incrementamos &lt;code&gt;head&lt;/code&gt;. Ese orden no es accidente. &lt;code&gt;head&lt;/code&gt; es la señal que le dice al consumidor que hay un byte nuevo disponible. Si incrementáramos &lt;code&gt;head&lt;/code&gt; primero y el tiempo de la interrupción se acomodara mal, el consumidor podría ver el nuevo &lt;code&gt;head&lt;/code&gt;, ir a leer esa ranura y sacar un byte que en realidad todavía no habíamos escrito. Al escribir la ranura antes de avanzar &lt;code&gt;head&lt;/code&gt;, garantizamos que en el instante en que el consumidor puede ver el byte, el byte de verdad ya está ahí. La actualización del índice publica los datos, así que va al final. &lt;code&gt;get&lt;/code&gt; hace el espejo: primero leemos el byte y después avanzamos &lt;code&gt;tail&lt;/code&gt;, así nunca marcamos una ranura como libre antes de habernos llevado su contenido.&lt;/p&gt;

&lt;p&gt;Un par de advertencias honestas para quien las quiera. Primero, &lt;code&gt;volatile&lt;/code&gt; nos da visibilidad, no atomicidad. Este diseño asume que leer o escribir &lt;code&gt;head&lt;/code&gt; y &lt;code&gt;tail&lt;/code&gt; sucede en una sola operación de máquina ininterrumpible, lo cual es cierto cuando son del tamaño de palabra en el objetivo. En un MCU de 8 bits, donde &lt;code&gt;size_t&lt;/code&gt; abarca varios bytes, la actualización de un índice podría partirse en varias instrucciones y quedar rota por una interrupción, y tendrías que tomarlo en cuenta. Segundo, en un microcontrolador de un solo núcleo con una ISR, &lt;code&gt;volatile&lt;/code&gt; más el orden de escribir y luego publicar es suficiente. En un sistema multinúcleo con ordenamiento de memoria débil recurrirías a operaciones atómicas con semántica explícita de adquisición y liberación, para garantizar que la escritura de la ranura se vea antes que la del índice. Para el caso clásico de ISR y bucle principal, que es para el que está hecho este búfer, estamos en terreno firme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cierre
&lt;/h2&gt;

&lt;p&gt;Eso es un búfer circular completo y funcional en un par de cientos de líneas de C. No hace asignación dinámica, da la vuelta con un solo AND a nivel de bits en lugar de una división, distingue lleno de vacío sin desperdiciar una ranura ni llevar un contador aparte, y es seguro llenarlo desde una interrupción mientras lo vaciamos desde el bucle principal. Para una estructura tan pequeña, trae metida una cantidad sorprendente de sutilezas.&lt;/p&gt;

&lt;p&gt;Vale la pena ser honesto sobre lo que este diseño no hace, porque cada una de estas cosas es un intercambio deliberado, no un descuido:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;La capacidad debe ser potencia de dos.&lt;/strong&gt; Este es el precio de cambiar el módulo por una máscara. Si necesitas guardar exactamente 100 bytes, redondeas a 128 y desperdicias un poco de espacio. En un sistema embebido casi siempre es un intercambio que conviene.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solo un productor y un consumidor.&lt;/strong&gt; Un escritor por índice es lo que lo hace libre de candados. Dos ISR escribiendo, o dos contextos leyendo, y necesitas sincronización de verdad.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No hay modo de sobrescribir lo más viejo.&lt;/strong&gt; Cuando el búfer está lleno, &lt;code&gt;put&lt;/code&gt; rechaza el byte nuevo en lugar de descartar el más viejo. Algunas aplicaciones quieren lo contrario, conservar las muestras más recientes y tirar las viejas. Esa es una variante razonable, solo que no es esta.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solo bytes.&lt;/strong&gt; Este búfer guarda &lt;code&gt;uint8_t&lt;/code&gt;. Guardar elementos más anchos, o structs arbitrarios, implica generalizar el tamaño del elemento, lo que cambia la aritmética de índices y el almacenamiento.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ninguna de estas limitaciones es difícil de levantar. Son las preguntas que salen naturalmente, y varias de ellas se convierten en sus propios patrones más adelante en esta serie.&lt;/p&gt;

&lt;p&gt;Y hablando de eso, el búfer circular es un cimiento, no un destino. Aparece por debajo de varios de los patrones que faltan: una cola de eventos es un búfer circular de mensajes en lugar de bytes, un filtro de promedio móvil es un búfer circular de muestras, y el despachador de comandos que construiremos lee su entrada directamente de uno. Lo que sigue en la serie es el pool de memoria. El búfer circular nos dio almacenamiento fijo para bytes; el pool de memoria nos da almacenamiento fijo para objetos, y es lo que permite que una cola de eventos guarde structs en lugar de datos crudos. Nos vemos ahí.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>embeddedsystems</category>
      <category>datastructures</category>
      <category>c</category>
    </item>
    <item>
      <title>Compendium: Reproducible Developer Environments in One Config</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Mon, 18 May 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/compendium-reproducible-developer-environments-in-one-config-3aon</link>
      <guid>https://dev.to/elcapitan/compendium-reproducible-developer-environments-in-one-config-3aon</guid>
      <description>&lt;h2&gt;
  
  
  Why I Built It
&lt;/h2&gt;

&lt;p&gt;Earlier this year I kept coming back to a project that had been on the back burner for a while. The original idea was an IDE with a built-in way to configure development environments, but the more I researched, the clearer it got that I was looking at a multi-year build. Not wanting to wait that long to ship something useful, I narrowed in on the feature I cared about most: the ability to configure and set up a development environment easily, without the IDE wrapped around it.&lt;/p&gt;

&lt;p&gt;Throughout my career, setting up toolchains and development environments has always been a painful experience: embedded cross-compilers that only build cleanly on the maintainer's exact OS version, READMEs that promise five minutes and take three days. I've also spent weeks building containers to wall off the development environment, only to watch the image balloon every time another tool got added. The documentation goes stale almost as fast as it gets written, because the tools moved on and nobody updated it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why C and C++ First
&lt;/h2&gt;

&lt;p&gt;For Compendium's first iteration I wanted to focus on the language where the setup pain runs deepest: C and C++. Every scripting language already has a version manager that works well enough. nvm handles Node, pyenv handles Python, asdf and mise cover most of the rest. None of them seriously handle GCC, clang, or arm-none-eabi-gcc. Embedded toolchains in particular are where I lose the most time, between USB drivers, OpenOCD configs, and compiler versions that have to match the linker script the project was tested against. Starting there meant solving the hardest case first. Adding Go, Python, and Node on top of that architecture turned out to be the easy part.&lt;/p&gt;

&lt;h2&gt;
  
  
  v0.1.1 Ships Today
&lt;/h2&gt;

&lt;p&gt;v0.1.1 ships today. The CLI is a single Go binary, the config is a single TOML file, and everything installs under &lt;code&gt;~/.local/compendium/&lt;/code&gt; without sudo. Rather than recap the docs here, I'll point you at them: &lt;a href="https://compendium.ilean.me/" rel="noopener noreferrer"&gt;compendium.ilean.me&lt;/a&gt; has the quick start, the full tool list, the config reference, and per-language guides for C/C++, Go, Python, Node, and embedded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLI&lt;/strong&gt; : Go (single static binary, no runtime dependencies)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration&lt;/strong&gt; : TOML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registry&lt;/strong&gt; : JSON manifests, hosted on GitHub&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platforms&lt;/strong&gt; : macOS (Apple Silicon, Intel), Linux (x86_64, arm64)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt; : GPL v3 (CLI), CC0 (registry)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;p&gt;v0.2 broadens C/C++ build system support to Autotools, adds Java and Rust, completes the embedded cross-compiler set with Xtensa for ESP32, and wires up the custom git hook runner. v0.3 introduces a Compendium-native C/C++ package manager. The full roadmap lives in the docs. If you want to follow along, contribute, or request a tool for the registry, GitHub issues and discussions are open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://compendium.ilean.me/" rel="noopener noreferrer"&gt;Compendium Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ileanmjr88/compendium" rel="noopener noreferrer"&gt;Compendium Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ileanmjr88/compendium-registry" rel="noopener noreferrer"&gt;Compendium Registry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>compendium</category>
      <category>devtools</category>
      <category>go</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Compendium: entornos de desarrollo reproducibles en una sola configuración</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Mon, 18 May 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/compendium-entornos-de-desarrollo-reproducibles-en-una-sola-configuracion-47i2</link>
      <guid>https://dev.to/elcapitan/compendium-entornos-de-desarrollo-reproducibles-en-una-sola-configuracion-47i2</guid>
      <description>&lt;h2&gt;
  
  
  Por qué lo construí
&lt;/h2&gt;

&lt;p&gt;A principios de este año seguía volviendo a un proyecto que llevaba un buen rato en pausa. La idea original era un IDE con una forma integrada de configurar entornos de desarrollo, pero entre más investigaba, más claro me quedaba que estaba frente a un desarrollo de varios años. Como no quería esperar tanto para lanzar algo útil, me enfoqué en la funcionalidad que más me importaba: poder configurar y preparar un entorno de desarrollo con facilidad, sin el IDE alrededor.&lt;/p&gt;

&lt;p&gt;A lo largo de mi carrera, configurar toolchains y entornos de desarrollo siempre ha sido una experiencia dolorosa: compiladores cruzados de embebidos que solo compilan limpiamente en la versión exacta del sistema operativo del mantenedor, READMEs que prometen cinco minutos y toman tres días. También he pasado semanas construyendo contenedores para aislar el entorno de desarrollo, solo para ver cómo la imagen se inflaba cada vez que se agregaba otra herramienta. La documentación queda obsoleta casi tan rápido como se escribe, porque las herramientas avanzaron y nadie la actualizó.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué primero C y C++
&lt;/h2&gt;

&lt;p&gt;Para la primera iteración de Compendium quise enfocarme en el lenguaje donde el dolor de la configuración es más profundo: C y C++. Todos los lenguajes de scripting ya tienen un gestor de versiones que funciona lo suficientemente bien. nvm maneja Node, pyenv maneja Python, asdf y mise cubren casi todo lo demás. Ninguno de ellos maneja en serio GCC, clang o arm-none-eabi-gcc. Los toolchains de embebidos en particular son donde más tiempo pierdo, entre drivers USB, configuraciones de OpenOCD y versiones de compilador que tienen que coincidir con el linker script contra el que se probó el proyecto. Empezar ahí significaba resolver primero el caso más difícil. Agregar Go, Python y Node sobre esa arquitectura resultó ser la parte fácil.&lt;/p&gt;

&lt;h2&gt;
  
  
  La v0.1.1 se lanza hoy
&lt;/h2&gt;

&lt;p&gt;La v0.1.1 se lanza hoy. La CLI es un único binario de Go, la configuración es un único archivo TOML y todo se instala en &lt;code&gt;~/.local/compendium/&lt;/code&gt; sin sudo. En lugar de repetir la documentación aquí, te dirijo a ella: &lt;a href="https://compendium.ilean.me/" rel="noopener noreferrer"&gt;compendium.ilean.me&lt;/a&gt; tiene la guía de inicio rápido, la lista completa de herramientas, la referencia de configuración y guías por lenguaje para C/C++, Go, Python, Node y embebidos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack tecnológico
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLI&lt;/strong&gt; : Go (un único binario estático, sin dependencias en tiempo de ejecución)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuración&lt;/strong&gt; : TOML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registro&lt;/strong&gt; : manifiestos JSON, alojados en GitHub&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plataformas&lt;/strong&gt; : macOS (Apple Silicon, Intel), Linux (x86_64, arm64)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Licencia&lt;/strong&gt; : GPL v3 (CLI), CC0 (registro)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hoja de ruta
&lt;/h2&gt;

&lt;p&gt;La v0.2 amplía el soporte de sistemas de build de C/C++ a Autotools, agrega Java y Rust, completa el conjunto de compiladores cruzados para embebidos con Xtensa para el ESP32 y conecta el ejecutor de git hooks personalizados. La v0.3 introduce un gestor de paquetes de C/C++ nativo de Compendium. La hoja de ruta completa vive en la documentación. Si quieres seguir el avance, contribuir o solicitar una herramienta para el registro, los issues y las discusiones de GitHub están abiertos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enlaces del proyecto
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://compendium.ilean.me/" rel="noopener noreferrer"&gt;Documentación de Compendium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ileanmjr88/compendium" rel="noopener noreferrer"&gt;Repositorio de Compendium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ileanmjr88/compendium-registry" rel="noopener noreferrer"&gt;Registro de Compendium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>spanish</category>
      <category>devtools</category>
      <category>go</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How I Built a Low-Friction Blog Pipeline</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Mon, 27 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/how-i-built-a-low-friction-blog-pipeline-1lof</link>
      <guid>https://dev.to/elcapitan/how-i-built-a-low-friction-blog-pipeline-1lof</guid>
      <description>&lt;h2&gt;
  
  
  Why Build a Pipeline?
&lt;/h2&gt;

&lt;p&gt;I wanted to grow my online presence, a dedicated corner of the internet to showcase my projects and skills, point people to my GitHub, LinkedIn, and socials, and share my thoughts on things I was building or learning. Simple enough goal. The execution, though, turned out to be a learning process.&lt;/p&gt;

&lt;p&gt;My first attempt at blogging was manual and messy. Write a post, publish it somewhere, copy it somewhere else, and hope people find it. I had set up the Dev.to RSS feed fetching early on, but that was about it. There was no real visibility into whether anyone was reading, and no reason to keep going. Unsurprisingly, I took a year-long gap between posts, not because I stopped having things to say, but because the friction was too high and life got in the way. When I started a new job, I focused on learning my new responsibilities and let the blog quietly go dormant.&lt;/p&gt;

&lt;p&gt;When I came back to it, I wanted to do things differently. The goal was simple: write once, publish everywhere, no copy-pasting across platforms, no manual distribution, and some way to know if the work was actually reaching anyone. The RSS feed was already there but the imports were messy. The frontmatter in my blog posts didn't match the RSS schema, so every import came in inconsistent and making changes or adding new frontmatter fields meant manually fixing every post. What I did this time was get my Astro frontmatter to match the RSS schema properly, so imports were clean and uniform from the start. Now it was about connecting the remaining pieces into a proper pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Astro&lt;/strong&gt; Before launching my personal site, I had tried to build it with Next.js a year earlier. I didn't fully understand it at the time and kept running into hydration issues. At a local Code and Coffee meetup, I asked one of the attendees for advice and he pointed me to Astro. Within days of going through a tutorial, I had my site live for the first time. I paired it with Tailwind CSS to make theming easy to maintain and update down the road.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pexels&lt;/strong&gt; Every post needs a cover image. Pexels is my go-to for finding images, royalty-free stock photos and videos shared by creators. High quality, free to use, and no licensing headaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dev.to&lt;/strong&gt; This came much later in the process. After some digging I discovered that Astro can generate an &lt;code&gt;rss.xml&lt;/code&gt; feed if set up correctly, and Dev.to can periodically pull from that feed to automatically import new posts. All I need to do is keep my frontmatter in sync with what Dev.to expects from the feed, no manual copying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Umami&lt;/strong&gt; I wanted visibility into who was actually reading. After some research I landed on Umami: privacy-friendly, no cookie banners, and a free hobby tier that covers up to 3 websites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mastodon&lt;/strong&gt; After Twitter was acquired I decided to leave and delete my profile. Mastodon turned out to be a great fit, especially for the tech community, and the fediverse has a different energy that I appreciate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt; Where I manually share each post to take advantage of how LinkedIn's algorithm circulates content to your network and extended network for a few days after publishing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Astro
&lt;/h2&gt;

&lt;p&gt;Each blog post is written in Markdown format, which Astro handles exceptionally well out of the box. One of the keys to making everything work smoothly is Astro's content collections configuration, this is how you tell Astro the shape of your content so it can validate and query it consistently. For this project I have Markdown files for blog entries and JSON files to define my projects. The &lt;a href="https://docs.astro.build/en/guides/content-collections/" rel="noopener noreferrer"&gt;Astro content collections documentation&lt;/a&gt; is worth reading carefully to understand how to define your schema.&lt;/p&gt;

&lt;p&gt;Before looking at the frontmatter, it helps to see the schema that enforces it. This lives in &lt;code&gt;src/content.config.ts&lt;/code&gt; and uses Astro's content collections with Zod validation. If a frontmatter field is missing or the wrong type, Astro will throw an error at build time rather than silently generating a broken RSS feed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineCollection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro:content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;glob&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro/loaders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineCollection&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/content/blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;pubDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each blog post starts with a frontmatter block that drives everything downstream, from the RSS feed to Dev.to imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Building&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Terminal&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Text&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Editor:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;View&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(Part&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;3)"&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;In&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Part&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;building&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;wordNebula,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cover&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;View&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;layer,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;why&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;chose&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;FTXUI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;over&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ncurses..."&lt;/span&gt;
&lt;span class="na"&gt;pubDate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-04-05&lt;/span&gt;
&lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ilean&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Monterrubio&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jr"&lt;/span&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/src/content/images/pexels-markus-winkler-1430818-4065400.jpg'&lt;/span&gt;
&lt;span class="na"&gt;categories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cpp'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;terminal'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;architecture'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;programming'&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping this frontmatter consistent and aligned with the RSS schema is what makes the rest of the pipeline work without any manual cleanup. This also makes the &lt;code&gt;rss.xml.ts&lt;/code&gt; configuration straightforward to set up. All it needs to do is read the frontmatter content and it will generate the &lt;code&gt;rss.xml&lt;/code&gt; file at build time.&lt;/p&gt;

&lt;p&gt;Here is what a basic &lt;code&gt;rss.xml.ts&lt;/code&gt; file looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;rss&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@astrojs/rss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro:content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;rss&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your Blog Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your blog description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;site&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;pubDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pubDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/blog/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fields in the &lt;code&gt;items&lt;/code&gt; map directly to the frontmatter fields you define. As long as the frontmatter is consistent, the RSS output will be clean and Dev.to will be able to import it without any issues. For additional information visit the &lt;a href="https://docs.astro.build/en/recipes/rss/" rel="noopener noreferrer"&gt;Astro RSS documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto-Importing to Dev.to via RSS
&lt;/h2&gt;

&lt;p&gt;For this part you will need a Dev.to account. Once your website is live and the RSS feed URL is active, navigate to your Dev.to dashboard. On the left-hand sidebar you will find &lt;strong&gt;RSS Import Feeds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fca2t9mhnrayjf5rd9muk.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fca2t9mhnrayjf5rd9muk.webp" alt="Dev.to RSS feed import setup screen" width="800" height="643"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there, click &lt;strong&gt;+ Add a Feed Source&lt;/strong&gt; to expand the form. Enter your &lt;code&gt;rss.xml&lt;/code&gt; URL in the RSS Feed URL field. A couple of settings worth paying attention to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mark the RSS source as canonical URL by default:&lt;/strong&gt; keep this checked. This is one of the most important settings in the whole pipeline. It tells Dev.to that your personal site is the original source of the content, which means search engines will credit your site and not the Dev.to version. Get this wrong and Dev.to can end up outranking your own site for your own writing. Always leave this on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace self-referential links with DEV Community-specific links:&lt;/strong&gt; leave this unchecked unless you are migrating your entire blog to Dev.to permanently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you click &lt;strong&gt;Add Feed Source&lt;/strong&gt; , Dev.to will periodically check your feed and pull in any new posts. Imported posts land as &lt;strong&gt;drafts&lt;/strong&gt; , so before publishing you will want to review each one and add a &lt;strong&gt;series name&lt;/strong&gt; if it is part of a multi-part post, pick &lt;strong&gt;tags&lt;/strong&gt; that match popular Dev.to tags for discoverability (for example: &lt;code&gt;cpp&lt;/code&gt;, &lt;code&gt;terminal&lt;/code&gt;, &lt;code&gt;architecture&lt;/code&gt;, &lt;code&gt;programming&lt;/code&gt;), and add a &lt;strong&gt;cover image&lt;/strong&gt; sourced from Pexels. When you are ready to go live, open the post editor and flip &lt;code&gt;published&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; in the Dev.to frontmatter at the top of the post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mastodon Verification and Author Attribution
&lt;/h2&gt;

&lt;p&gt;I wanted to establish a presence on Mastodon, and it turns out Astro makes it easy to connect your blog to the fediverse with just two small additions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verification with rel="me"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mastodon uses the &lt;code&gt;rel="me"&lt;/code&gt; link standard to verify that you own a website. Adding it to your Astro layout head tag gives you the green checkmark on your Mastodon profile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"me"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://mastodon.social/@yourusername"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Author Attribution with fediverse:creator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fediverse:creator&lt;/code&gt; meta tag is a newer addition that connects your blog posts to your Mastodon identity. When someone shares one of your posts on Mastodon, it automatically credits your account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"fediverse:creator"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"@yourusername@mastodon.social"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this to your blog post layout so it only appears on post pages rather than every page on the site. Together these two tags make your blog a proper fediverse citizen, verified, attributable, and discoverable by the tech community that has made Mastodon their home.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Privacy-Friendly Analytics with Umami
&lt;/h2&gt;

&lt;p&gt;When I started getting posts out I wanted to know if anyone was actually reading them. Google Analytics was the obvious choice but it felt like overkill, heavy, cookie-dependent, and requiring a consent banner just to get started. After some digging I found Umami, a lightweight privacy-friendly alternative that ticked every box. The free hobby tier covers up to 3 websites, 100K events per month, and 6 months of data retention. No cookies, no consent banners, no tracking across sites.&lt;/p&gt;

&lt;p&gt;Setup is straightforward. Once you create your Umami account and add your site, you get a script tag to drop into your Astro layout file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script
  &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cloud.umami.is/script.js"&lt;/span&gt;
  &lt;span class="na"&gt;data-website-id=&lt;/span&gt;&lt;span class="s"&gt;"your-website-id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this to your main &lt;code&gt;Layout.astro&lt;/code&gt; file and it will be included on every page automatically. I also added a small notice in the footer to let visitors know the site uses privacy-friendly analytics with no cookies or personal data collected, a small but honest touch that sets the right expectations.&lt;/p&gt;

&lt;p&gt;The Umami dashboard covers all the basics: visitors, page views, referrers, locations, devices, and bounce rate. It is everything you need to understand how your content is performing without compromising your readers' privacy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Publishing Workflow
&lt;/h2&gt;

&lt;p&gt;I typically write about something I worked on professionally, a personal project, or a topic I consider myself an expert in. Here is the end-to-end process I follow from idea to published post:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Write the draft in Notion&lt;/strong&gt; I started out using Google Docs but since returning to the blog I migrated to Notion. It handles code blocks and tables properly and exports to Markdown in a way that actually translates correctly, which makes the next step much smoother.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Generate the Astro-ready Markdown file with frontmatter&lt;/strong&gt; Once the draft is ready I convert it into a Markdown file with the correct frontmatter fields — title, description, pubDate, author, image, and categories — making sure everything aligns with the RSS schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Deploy to ilean.me on Sunday morning&lt;/strong&gt; Sunday is my publishing day. I push the new post and deploy the site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Dev.to picks up via RSS&lt;/strong&gt; Dev.to periodically polls the RSS feed URL, so the post will appear as a draft in my Dev.to dashboard within a few hours depending on when it last checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Post on Mastodon the same day&lt;/strong&gt; I share the post on Mastodon with hashtags, typically the same ones used in the post frontmatter for consistency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Schedule LinkedIn for Monday morning&lt;/strong&gt; LinkedIn has a post scheduling feature which has been a game changer. I schedule it to go live Monday morning at 8-9 AM, when recruiters and professionals are most active, letting LinkedIn's algorithm circulate it to my network and extended network over the following days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Review and publish the Dev.to draft&lt;/strong&gt; Once it lands in Dev.to I review it, add a series name if it is part of a multi-part post, set the tags, add a cover image from Pexels, and flip &lt;code&gt;published&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results So Far
&lt;/h2&gt;

&lt;p&gt;I revived the blog in February and March of 2026. On Dev.to, in February the most views I received in a single day was 16. Since then that number has climbed to 36 views in a single day. As of April 25th 2026 Dev.to shows 435 total views, though down 24% compared to the previous 7 days, a good reminder that consistency matters.&lt;/p&gt;

&lt;p&gt;On ilean.me, Umami shows 48 unique visitors so far. What surprised me was the geographic spread. A large portion of visitors are coming from Asia, which makes sense given that embedded systems and C++ are a common stack there. The majority are from the US, with California showing up frequently, which tracks given the tech industry concentration there.&lt;/p&gt;

&lt;p&gt;The numbers are small but the trend is encouraging. Search engine traffic compounds over time; every post indexed is another entry point for someone to find your work. The goal was never overnight virality, it was to slowly and consistently build an online presence, and the data shows that is exactly what is happening.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;A few honest lessons from getting this pipeline up and running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Double check your publication dates.&lt;/strong&gt; One post did not show up on Dev.to on time and after some digging I realized the date in the frontmatter was off by a day. Dev.to pulls from the RSS feed based on the pubDate field, so if that is wrong the post gets skipped or delayed. Always verify the date before deploying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be patient with the RSS fetch timing.&lt;/strong&gt; Dev.to does not poll your feed instantly. Depending on when it last checked, it could take several hours for a new post to appear as a draft. Do not panic and assume something is broken; just give it time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filter out your own visits.&lt;/strong&gt; Early on I got excited seeing a visitor from Houston in Umami before realizing it was just me clicking my own links. The free Umami tier does not have a built-in way to exclude your own IP, so just keep that in mind when reading your early numbers and take them with a grain of salt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find your community.&lt;/strong&gt; One thing I want to do going forward is find Houston based tech Discord servers with software engineers and developers to share my content in. Posting in relevant communities can accelerate growth in a way that passive RSS and social posts alone cannot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not check analytics too often.&lt;/strong&gt; It is tempting at the start but the numbers move slowly and checking constantly just creates unnecessary anxiety. Set a cadence, maybe once a week, and focus on writing the next post instead.&lt;/p&gt;

&lt;p&gt;The whole point of building this pipeline was to remove friction, and it worked. There is no year-long gap waiting to happen again; there is just the next post.&lt;/p&gt;

</description>
      <category>astro</category>
      <category>blogging</category>
      <category>devto</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Cómo construí un pipeline de blog sin fricciones</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Mon, 27 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/como-construi-un-pipeline-de-blog-sin-fricciones-1elk</link>
      <guid>https://dev.to/elcapitan/como-construi-un-pipeline-de-blog-sin-fricciones-1elk</guid>
      <description>&lt;h2&gt;
  
  
  ¿Por qué construir un pipeline?
&lt;/h2&gt;

&lt;p&gt;Quería hacer crecer mi presencia en línea: un rincón propio de internet para mostrar mis proyectos y habilidades, dirigir a la gente a mi GitHub, LinkedIn y redes sociales, y compartir mis ideas sobre lo que estaba construyendo o aprendiendo. Un objetivo bastante sencillo. La ejecución, sin embargo, resultó ser todo un proceso de aprendizaje.&lt;/p&gt;

&lt;p&gt;Mi primer intento de bloguear fue manual y desordenado. Escribir una entrada, publicarla en algún lado, copiarla a otro y esperar que la gente la encontrara. Había configurado la obtención del feed RSS de Dev.to desde el principio, pero hasta ahí llegaba. No había una visibilidad real de si alguien estaba leyendo, ni una razón para seguir. Como era de esperarse, dejé pasar un año entero entre entradas, no porque dejara de tener cosas que decir, sino porque la fricción era demasiado alta y la vida se atravesó. Cuando empecé un nuevo trabajo, me concentré en aprender mis nuevas responsabilidades y dejé que el blog quedara en silencio.&lt;/p&gt;

&lt;p&gt;Cuando volví a retomarlo, quería hacer las cosas de otra manera. El objetivo era simple: escribir una vez, publicar en todas partes, sin copiar y pegar entre plataformas, sin distribución manual, y con alguna forma de saber si el trabajo realmente llegaba a alguien. El feed RSS ya estaba ahí, pero las importaciones eran un desorden. El frontmatter de mis entradas no coincidía con el esquema del RSS, así que cada importación llegaba inconsistente, y hacer cambios o agregar nuevos campos al frontmatter significaba corregir cada entrada a mano. Esta vez lo que hice fue lograr que el frontmatter de Astro coincidiera correctamente con el esquema del RSS, para que las importaciones fueran limpias y uniformes desde el inicio. Ahora se trataba de conectar las piezas restantes en un pipeline en forma.&lt;/p&gt;

&lt;h2&gt;
  
  
  El stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Astro&lt;/strong&gt; Antes de lanzar mi sitio personal, había intentado construirlo con Next.js un año antes. En ese momento no lo entendía del todo y me topaba constantemente con problemas de hidratación. En un meetup local de Code and Coffee le pedí consejo a uno de los asistentes y me recomendó Astro. A los pocos días de seguir un tutorial, tenía mi sitio en vivo por primera vez. Lo combiné con Tailwind CSS para que el manejo de temas fuera fácil de mantener y actualizar más adelante.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pexels&lt;/strong&gt; Cada entrada necesita una imagen de portada. Pexels es mi opción de cabecera para encontrar imágenes: fotos y videos de stock libres de regalías compartidos por creadores. De alta calidad, gratis y sin dolores de cabeza por licencias.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dev.to&lt;/strong&gt; Esto llegó mucho después en el proceso. Tras investigar un poco, descubrí que Astro puede generar un feed &lt;code&gt;rss.xml&lt;/code&gt; si se configura correctamente, y que Dev.to puede consultar ese feed de forma periódica para importar automáticamente las nuevas entradas. Lo único que tengo que hacer es mantener mi frontmatter en sincronía con lo que Dev.to espera del feed, sin copiar nada a mano.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Umami&lt;/strong&gt; Quería tener visibilidad de quién estaba leyendo realmente. Después de investigar, me decidí por Umami: respetuoso con la privacidad, sin banners de cookies y con un plan gratuito para aficionados que cubre hasta 3 sitios web.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mastodon&lt;/strong&gt; Después de que adquirieran Twitter, decidí irme y eliminar mi perfil. Mastodon resultó encajar muy bien, sobre todo para la comunidad tech, y el fediverso tiene una energía distinta que aprecio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt; Donde comparto manualmente cada entrada para aprovechar cómo el algoritmo de LinkedIn hace circular el contenido por tu red y tu red extendida durante unos días después de publicar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configurando Astro
&lt;/h2&gt;

&lt;p&gt;Cada entrada del blog está escrita en formato Markdown, que Astro maneja excepcionalmente bien desde el inicio. Una de las claves para que todo funcione sin problemas es la configuración de las content collections de Astro; así es como le dices a Astro la forma de tu contenido para que pueda validarlo y consultarlo de manera consistente. Para este proyecto tengo archivos Markdown para las entradas del blog y archivos JSON para definir mis proyectos. Vale la pena leer con atención la &lt;a href="https://docs.astro.build/en/guides/content-collections/" rel="noopener noreferrer"&gt;documentación de content collections de Astro&lt;/a&gt; para entender cómo definir tu esquema.&lt;/p&gt;

&lt;p&gt;Antes de ver el frontmatter, ayuda conocer el esquema que lo hace cumplir. Vive en &lt;code&gt;src/content.config.ts&lt;/code&gt; y usa las content collections de Astro con validación de Zod. Si falta un campo del frontmatter o es del tipo incorrecto, Astro lanzará un error en tiempo de compilación en lugar de generar en silencio un feed RSS roto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineCollection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro:content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;glob&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro/loaders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineCollection&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/content/blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;pubDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada entrada del blog comienza con un bloque de frontmatter que impulsa todo lo que viene después, desde el feed RSS hasta las importaciones a Dev.to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Building&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Terminal&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Text&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Editor:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;View&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(Part&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;3)"&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;In&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Part&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;building&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;wordNebula,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cover&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;View&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;layer,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;why&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;chose&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;FTXUI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;over&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ncurses..."&lt;/span&gt;
&lt;span class="na"&gt;pubDate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-04-05&lt;/span&gt;
&lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ilean&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Monterrubio&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Jr"&lt;/span&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/src/content/images/pexels-markus-winkler-1430818-4065400.jpg'&lt;/span&gt;
&lt;span class="na"&gt;categories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cpp'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;terminal'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;architecture'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;programming'&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mantener este frontmatter consistente y alineado con el esquema del RSS es lo que hace que el resto del pipeline funcione sin ninguna limpieza manual. Esto también hace que la configuración de &lt;code&gt;rss.xml.ts&lt;/code&gt; sea sencilla de armar. Lo único que necesita hacer es leer el contenido del frontmatter y generará el archivo &lt;code&gt;rss.xml&lt;/code&gt; en tiempo de compilación.&lt;/p&gt;

&lt;p&gt;Así se ve un archivo &lt;code&gt;rss.xml.ts&lt;/code&gt; básico:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;rss&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@astrojs/rss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCollection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;astro:content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;rss&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your Blog Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your blog description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;site&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;pubDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pubDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/blog/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los campos del &lt;code&gt;items&lt;/code&gt; se corresponden directamente con los campos del frontmatter que definas. Mientras el frontmatter sea consistente, la salida del RSS será limpia y Dev.to podrá importarlo sin ningún problema. Para más información, visita la &lt;a href="https://docs.astro.build/en/recipes/rss/" rel="noopener noreferrer"&gt;documentación de RSS de Astro&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Importación automática a Dev.to vía RSS
&lt;/h2&gt;

&lt;p&gt;Para esta parte necesitarás una cuenta de Dev.to. Una vez que tu sitio web esté en vivo y la URL del feed RSS esté activa, ve a tu panel de Dev.to. En la barra lateral izquierda encontrarás &lt;strong&gt;RSS Import Feeds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.ilean.me%2Fes%2Fblog%2Fimages%2Fdevto_rss_setup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.ilean.me%2Fes%2Fblog%2Fimages%2Fdevto_rss_setup.png" alt="Pantalla de configuración de importación del feed RSS de Dev.to"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Desde ahí, haz clic en &lt;strong&gt;+ Add a Feed Source&lt;/strong&gt; para desplegar el formulario. Ingresa la URL de tu &lt;code&gt;rss.xml&lt;/code&gt; en el campo RSS Feed URL. Un par de ajustes a los que vale la pena prestar atención:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mark the RSS source as canonical URL by default:&lt;/strong&gt; déjalo activado. Este es uno de los ajustes más importantes de todo el pipeline. Le indica a Dev.to que tu sitio personal es la fuente original del contenido, lo que significa que los motores de búsqueda darán crédito a tu sitio y no a la versión de Dev.to. Si te equivocas en esto, Dev.to puede terminar posicionándose por encima de tu propio sitio con tus propios textos. Déjalo siempre activado.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace self-referential links with DEV Community-specific links:&lt;/strong&gt; déjalo desactivado, a menos que estés migrando todo tu blog a Dev.to de forma permanente.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Una vez que hagas clic en &lt;strong&gt;Add Feed Source&lt;/strong&gt; , Dev.to revisará tu feed periódicamente e importará cualquier entrada nueva. Las entradas importadas llegan como &lt;strong&gt;borradores&lt;/strong&gt; , así que antes de publicar conviene revisar cada una y agregar un &lt;strong&gt;nombre de serie&lt;/strong&gt; si forma parte de una entrada de varias partes, elegir &lt;strong&gt;etiquetas&lt;/strong&gt; que coincidan con las etiquetas populares de Dev.to para que sea más fácil de descubrir (por ejemplo: &lt;code&gt;cpp&lt;/code&gt;, &lt;code&gt;terminal&lt;/code&gt;, &lt;code&gt;architecture&lt;/code&gt;, &lt;code&gt;programming&lt;/code&gt;) y agregar una &lt;strong&gt;imagen de portada&lt;/strong&gt; tomada de Pexels. Cuando estés listo para publicar, abre el editor de la entrada y cambia &lt;code&gt;published&lt;/code&gt; a &lt;code&gt;true&lt;/code&gt; en el frontmatter de Dev.to al inicio de la entrada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verificación con Mastodon y atribución de autoría
&lt;/h2&gt;

&lt;p&gt;Quería establecer una presencia en Mastodon, y resulta que Astro facilita conectar tu blog al fediverso con solo dos pequeñas adiciones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verificación con rel="me"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mastodon usa el estándar de enlace &lt;code&gt;rel="me"&lt;/code&gt; para verificar que eres dueño de un sitio web. Agregarlo a la etiqueta head de tu layout de Astro te da la palomita verde en tu perfil de Mastodon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"me"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://mastodon.social/@yourusername"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Atribución de autoría con fediverse:creator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;La meta etiqueta &lt;code&gt;fediverse:creator&lt;/code&gt; es una adición más reciente que conecta las entradas de tu blog con tu identidad de Mastodon. Cuando alguien comparte una de tus entradas en Mastodon, le da crédito a tu cuenta automáticamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"fediverse:creator"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"@yourusername@mastodon.social"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agrégalo al layout de tus entradas para que solo aparezca en las páginas de las entradas y no en todas las páginas del sitio. Juntas, estas dos etiquetas convierten a tu blog en un ciudadano del fediverso en forma: verificado, atribuible y fácil de descubrir por la comunidad tech que ha hecho de Mastodon su hogar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agregando analíticas respetuosas con la privacidad con Umami
&lt;/h2&gt;

&lt;p&gt;Cuando empecé a sacar entradas, quería saber si alguien las estaba leyendo de verdad. Google Analytics era la opción obvia, pero se sentía excesivo: pesado, dependiente de cookies y que requería un banner de consentimiento solo para empezar. Tras investigar un poco encontré Umami, una alternativa ligera y respetuosa con la privacidad que cumplía con todo. El plan gratuito para aficionados cubre hasta 3 sitios web, 100 mil eventos al mes y 6 meses de retención de datos. Sin cookies, sin banners de consentimiento, sin rastreo entre sitios.&lt;/p&gt;

&lt;p&gt;La configuración es sencilla. Una vez que creas tu cuenta de Umami y agregas tu sitio, obtienes una etiqueta de script para colocar en el archivo de layout de Astro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script
  &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cloud.umami.is/script.js"&lt;/span&gt;
  &lt;span class="na"&gt;data-website-id=&lt;/span&gt;&lt;span class="s"&gt;"your-website-id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agrégalo a tu archivo principal &lt;code&gt;Layout.astro&lt;/code&gt; y se incluirá automáticamente en cada página. También agregué un pequeño aviso en el pie de página para que los visitantes sepan que el sitio usa analíticas respetuosas con la privacidad, sin cookies ni recolección de datos personales; un detalle pequeño pero honesto que establece las expectativas correctas.&lt;/p&gt;

&lt;p&gt;El panel de Umami cubre todo lo básico: visitantes, vistas de página, referencias, ubicaciones, dispositivos y tasa de rebote. Es todo lo que necesitas para entender cómo está funcionando tu contenido sin comprometer la privacidad de tus lectores.&lt;/p&gt;

&lt;h2&gt;
  
  
  El flujo de publicación
&lt;/h2&gt;

&lt;p&gt;Normalmente escribo sobre algo en lo que trabajé profesionalmente, un proyecto personal o un tema en el que me considero experto. Este es el proceso de principio a fin que sigo desde la idea hasta la entrada publicada:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Escribir el borrador en Notion&lt;/strong&gt; Empecé usando Google Docs, pero desde que retomé el blog me migré a Notion. Maneja bien los bloques de código y las tablas, y exporta a Markdown de una forma que realmente se traduce correctamente, lo que hace que el siguiente paso sea mucho más fluido.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Generar el archivo Markdown listo para Astro con frontmatter&lt;/strong&gt; Una vez que el borrador está listo, lo convierto en un archivo Markdown con los campos de frontmatter correctos —title, description, pubDate, author, image y categories— asegurándome de que todo se alinee con el esquema del RSS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Desplegar en ilean.me el domingo por la mañana&lt;/strong&gt; El domingo es mi día de publicación. Subo la nueva entrada y despliego el sitio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Dev.to la recoge vía RSS&lt;/strong&gt; Dev.to consulta periódicamente la URL del feed RSS, así que la entrada aparecerá como borrador en mi panel de Dev.to en unas horas, dependiendo de cuándo revisó por última vez.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Publicar en Mastodon el mismo día&lt;/strong&gt; Comparto la entrada en Mastodon con hashtags, normalmente los mismos que uso en el frontmatter de la entrada para mantener la consistencia.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Programar LinkedIn para el lunes por la mañana&lt;/strong&gt; LinkedIn tiene una función para programar publicaciones que ha sido un cambio total. La programo para que salga el lunes por la mañana entre las 8 y 9 AM, cuando los reclutadores y profesionales están más activos, dejando que el algoritmo de LinkedIn la haga circular por mi red y mi red extendida durante los días siguientes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Revisar y publicar el borrador de Dev.to&lt;/strong&gt; Una vez que llega a Dev.to, lo reviso, agrego un nombre de serie si forma parte de una entrada de varias partes, configuro las etiquetas, agrego una imagen de portada de Pexels y cambio &lt;code&gt;published&lt;/code&gt; a &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados hasta ahora
&lt;/h2&gt;

&lt;p&gt;Reviví el blog en febrero y marzo de 2026. En Dev.to, en febrero la mayor cantidad de vistas que recibí en un solo día fue 16. Desde entonces ese número subió a 36 vistas en un solo día. Al 25 de abril de 2026, Dev.to muestra 435 vistas totales, aunque con una baja del 24 % respecto a los 7 días anteriores, un buen recordatorio de que la consistencia importa.&lt;/p&gt;

&lt;p&gt;En ilean.me, Umami muestra 48 visitantes únicos hasta ahora. Lo que me sorprendió fue la distribución geográfica. Una buena parte de los visitantes viene de Asia, lo cual tiene sentido dado que los sistemas embebidos y C++ son un stack común allá. La mayoría viene de Estados Unidos, con California apareciendo con frecuencia, lo que cuadra dada la concentración de la industria tecnológica ahí.&lt;/p&gt;

&lt;p&gt;Los números son pequeños, pero la tendencia es alentadora. El tráfico de los motores de búsqueda se acumula con el tiempo; cada entrada indexada es otro punto de entrada para que alguien encuentre tu trabajo. El objetivo nunca fue volverse viral de la noche a la mañana, sino construir una presencia en línea de forma lenta y constante, y los datos muestran que es exactamente lo que está pasando.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué haría diferente
&lt;/h2&gt;

&lt;p&gt;Algunas lecciones honestas de poner este pipeline en marcha.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verifica dos veces tus fechas de publicación.&lt;/strong&gt; Una entrada no apareció a tiempo en Dev.to y, tras investigar un poco, me di cuenta de que la fecha en el frontmatter estaba desfasada por un día. Dev.to toma del feed RSS según el campo pubDate, así que si está mal, la entrada se omite o se retrasa. Verifica siempre la fecha antes de desplegar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ten paciencia con los tiempos de obtención del RSS.&lt;/strong&gt; Dev.to no consulta tu feed al instante. Dependiendo de cuándo revisó por última vez, una entrada nueva puede tardar varias horas en aparecer como borrador. No entres en pánico ni asumas que algo está roto; solo dale tiempo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filtra tus propias visitas.&lt;/strong&gt; Al principio me emocioné al ver un visitante de Houston en Umami, antes de darme cuenta de que era yo haciendo clic en mis propios enlaces. El plan gratuito de Umami no tiene una forma integrada de excluir tu propia IP, así que tenlo en cuenta al leer tus primeros números y tómalos con cautela.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encuentra tu comunidad.&lt;/strong&gt; Algo que quiero hacer de aquí en adelante es encontrar servidores de Discord tech de Houston con ingenieros de software y desarrolladores donde compartir mi contenido. Publicar en comunidades relevantes puede acelerar el crecimiento de una forma que el RSS pasivo y las publicaciones en redes por sí solos no pueden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No revises las analíticas con demasiada frecuencia.&lt;/strong&gt; Es tentador al principio, pero los números se mueven despacio y revisarlos constantemente solo genera ansiedad innecesaria. Establece una frecuencia, tal vez una vez por semana, y mejor concéntrate en escribir la siguiente entrada.&lt;/p&gt;

&lt;p&gt;Todo el propósito de construir este pipeline era eliminar la fricción, y funcionó. Ya no hay un hueco de un año esperando a repetirse; solo está la siguiente entrada.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>astro</category>
      <category>blogging</category>
      <category>devto</category>
    </item>
    <item>
      <title>Building a Terminal Text Editor: The View (Part 3)</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/building-a-terminal-text-editor-the-view-part-3-12gd</link>
      <guid>https://dev.to/elcapitan/building-a-terminal-text-editor-the-view-part-3-12gd</guid>
      <description>&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://www.ilean.me/blog/building-a-terminal-text-editor-the-model-part-1/" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;, I talked about the origin of this project, the early architectural decisions, and why I picked the GapBuffer with &lt;code&gt;IBuffer&lt;/code&gt; as the contract for all future buffer implementations. In &lt;a href="https://www.ilean.me/blog/building-a-terminal-text-editor-the-presenter-part-2/" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt;, we got into the details of the Presenter, how it wires the components together, handles user input, manages state, and keeps the Model and View in sync. Now we can move on to the View.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the View?
&lt;/h2&gt;

&lt;p&gt;The View is the component that the user interacts with directly. All user inputs are collected and passed to the Presenter, the View doesn't decide what to do with them. It is also responsible for rendering the text the user types, along with any warnings, statistics like word count, and whether the file is unsaved. The View doesn't calculate any of this, it just displays whatever the Presenter sends it. Another example of how all three parts of the Model-View-Presenter separate concerns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why FTXUI instead of ncurses
&lt;/h2&gt;

&lt;p&gt;In the proof of concept testing, I used ncurses, the de facto standard library for building terminal interfaces. It worked well for the initial testing, and I see a lot of projects still using it; it's tried and true. While ncurses would have been a great choice, I did a bit more research and found FTXUI, a modern C++ library with components to build interactive terminal user interfaces. I wanted to leverage that it uses modern C++, matching the project's early defined specification of targeting C++20.&lt;/p&gt;

&lt;h2&gt;
  
  
  The IView Interface
&lt;/h2&gt;

&lt;p&gt;While implementing the other components (Model and Presenter), I mocked up a thin interface for the View, &lt;code&gt;IView&lt;/code&gt;. At first, it was only taking character input and not accounting for special characters, and it didn't handle rendering any data. The proof of concept View just took the input, sent it to the Presenter, which would send it to the buffer and log it so I could see the data was actually flowing through the system.&lt;/p&gt;

&lt;p&gt;I was thinking of removing it, but after doing more research, I decided to expand the interface instead. Keeping &lt;code&gt;IView&lt;/code&gt; as an abstract class means that if someone wants to write their own View for an OS that isn't supported by FTXUI, they can implement the interface and plug it in without touching the Presenter or Model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;IView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ViewState&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getTerminalSize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;showMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick explanation, &lt;code&gt;run()&lt;/code&gt; starts the event loop and takes a callback for handling input, this is what the Presenter calls in its main loop. &lt;code&gt;render()&lt;/code&gt; takes the &lt;code&gt;ViewState&lt;/code&gt; we saw in Part 2 and draws it to the screen. &lt;code&gt;exit()&lt;/code&gt; and &lt;code&gt;getTerminalSize()&lt;/code&gt; do what they say, and &lt;code&gt;showMessage()&lt;/code&gt; is how the Presenter displays warnings like the "unsaved changes" prompt from the exit flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering the UI
&lt;/h2&gt;

&lt;p&gt;As we showed in Part 2, the &lt;code&gt;ViewState&lt;/code&gt; struct defines the contract for how the Presenter sends data to the View:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;ViewState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Content&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;visibleText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Text currently visible in viewport&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cursorPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Linear position of cursor in visibleText&lt;/span&gt;

    &lt;span class="c1"&gt;// Status bar information&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;wordCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Total word count (primary metric for writers)&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Current file name (or "Untitled")&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isDirty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Unsaved changes indicator&lt;/span&gt;

    &lt;span class="c1"&gt;// UI state&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Temporary status message (e.g., "Saved", "Error: ...")&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;showHelp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Whether to show help overlay&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three major types of data in the &lt;code&gt;ViewState&lt;/code&gt; contract: &lt;strong&gt;Content&lt;/strong&gt; , which tells us the &lt;code&gt;cursorPosition&lt;/code&gt; and the &lt;code&gt;visibleText&lt;/code&gt;. &lt;strong&gt;Status bar information&lt;/strong&gt; : &lt;code&gt;wordCount&lt;/code&gt;, &lt;code&gt;filename&lt;/code&gt;, and &lt;code&gt;isDirty&lt;/code&gt; which indicates whether the file is unsaved. Finally, the &lt;strong&gt;UI state&lt;/strong&gt; : &lt;code&gt;statusMessage&lt;/code&gt; and &lt;code&gt;showHelp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One of the biggest challenges was how to deal with the cursor, how to move it, and be able to add more to the buffer when the cursor moves. One of the simplest solutions is to break the &lt;code&gt;visibleText&lt;/code&gt; into three components: beforeCursor, cursor, and afterCursor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Element&lt;/span&gt; &lt;span class="n"&gt;FtxuiView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;renderEditor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibleText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursorPosition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Character at cursor with cyan background&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cursorChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursorChar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;bgcolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cyan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Black&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hbox&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cursor is highlighted with a cyan background so the user can see where they are in the text. This challenge existed in both ncurses and FTXUI, neither library gives you a built-in cursor for a custom text editor. You have to simulate it yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Translating Keyboard Input
&lt;/h2&gt;

&lt;p&gt;The View is also responsible for translating raw keyboard events into the &lt;code&gt;InputEvent&lt;/code&gt; structs that the Presenter understands. FTXUI has its own event system, so the View needs a translation layer between what FTXUI gives us and what our Presenter expects. The &lt;code&gt;translateEvent()&lt;/code&gt; method handles this mapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="n"&gt;FtxuiView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;translateEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ArrowLeft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ARROW_LEFT&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ArrowRight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ARROW_RIGHT&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Backspace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BACKSPACE&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ENTER&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CtrlQ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_Q&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CtrlS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_S&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Printable character&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_character&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="n"&gt;ie&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CHARACTER&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;ie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern is straightforward, each FTXUI event maps to one of our &lt;code&gt;InputEvent&lt;/code&gt; types. If it's a printable character, we extract the character value. If we don't recognize the event, it returns &lt;code&gt;UNKNOWN&lt;/code&gt; and gets ignored. This is the same translation layer that would need to be reimplemented if someone wrote a new View using a different library, another benefit of the &lt;code&gt;IView&lt;/code&gt; interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the View Can Do So Far
&lt;/h2&gt;

&lt;p&gt;The View works well for the current state of the project. It renders text as the user types, displays the status bar with the filename, word count, and unsaved changes indicator, captures all input events and routes them to the Presenter, and toggles the help overlay with F1. It is not perfect, newlines don't render yet and the arrow keys can't fully navigate up and down through the text. But for Phase 1, it does its job as the passive layer in the MVP pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up the Series
&lt;/h2&gt;

&lt;p&gt;This wraps up the three-part series on building wordNebula's Phase 1. Across these posts, we went from the origin of the project and why I chose Model-View-Presenter, through the Model with the GapBuffer and IBuffer interface, the Presenter that orchestrates everything and manages state, and finally the View that renders it all to the terminal.&lt;/p&gt;

&lt;p&gt;Building each layer independently and connecting them through contracts like &lt;code&gt;IBuffer&lt;/code&gt; and &lt;code&gt;IView&lt;/code&gt; made the project manageable. I could work on one piece at a time without worrying about breaking the others. That separation is the whole point of MVP, and it paid off.&lt;/p&gt;

&lt;p&gt;Phase 1 is complete but far from finished. There's more to build, but for now I'm stepping away to focus on other projects. If you want to explore the code, check out the project on &lt;a href="https://github.com/ileanmjr88/wordNebula" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>terminal</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>Construyendo un editor de texto de terminal: la Vista (Parte 3)</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Sun, 05 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/construyendo-un-editor-de-texto-de-terminal-la-vista-parte-3-2d1e</link>
      <guid>https://dev.to/elcapitan/construyendo-un-editor-de-texto-de-terminal-la-vista-parte-3-2d1e</guid>
      <description>&lt;h2&gt;
  
  
  Resumen
&lt;/h2&gt;

&lt;p&gt;En la &lt;a href="https://dev.to/es/blog/construyendo-un-editor-de-texto-de-terminal-el-modelo-parte-1/"&gt;Parte 1&lt;/a&gt; hablé del origen de este proyecto, las primeras decisiones de arquitectura y por qué elegí el GapBuffer con &lt;code&gt;IBuffer&lt;/code&gt; como el contrato para todas las futuras implementaciones de búfer. En la &lt;a href="https://dev.to/es/blog/construyendo-un-editor-de-texto-de-terminal-el-presentador-parte-2/"&gt;Parte 2&lt;/a&gt; entramos en los detalles del Presentador, cómo conecta los componentes, maneja la entrada del usuario, gestiona el estado y mantiene el Modelo y la Vista sincronizados. Ahora podemos pasar a la Vista.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es la Vista?
&lt;/h2&gt;

&lt;p&gt;La Vista es el componente con el que el usuario interactúa directamente. Todas las entradas del usuario se recopilan y se pasan al Presentador; la Vista no decide qué hacer con ellas. También es responsable de renderizar el texto que el usuario escribe, junto con cualquier advertencia, estadísticas como el conteo de palabras y si el archivo está sin guardar. La Vista no calcula nada de esto, solo muestra lo que el Presentador le envía. Otro ejemplo de cómo las tres partes de Modelo-Vista-Presentador separan las responsabilidades.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué FTXUI en lugar de ncurses
&lt;/h2&gt;

&lt;p&gt;En las pruebas de la prueba de concepto usé ncurses, la biblioteca estándar de facto para construir interfaces de terminal. Funcionó bien para las pruebas iniciales, y veo muchos proyectos que todavía la usan; es confiable y probada. Aunque ncurses habría sido una gran elección, investigué un poco más y encontré FTXUI, una biblioteca de C++ moderna con componentes para construir interfaces de usuario de terminal interactivas. Quería aprovechar que usa C++ moderno, en línea con la especificación que definí al inicio del proyecto de apuntar a C++20.&lt;/p&gt;

&lt;h2&gt;
  
  
  La interfaz IView
&lt;/h2&gt;

&lt;p&gt;Mientras implementaba los otros componentes (el Modelo y el Presentador), armé una interfaz ligera para la Vista, &lt;code&gt;IView&lt;/code&gt;. Al principio solo tomaba la entrada de caracteres y no contemplaba los caracteres especiales, ni se encargaba de renderizar dato alguno. La Vista de la prueba de concepto solo tomaba la entrada, la enviaba al Presentador, que la mandaba al búfer y la registraba para que yo pudiera ver que los datos realmente fluían por el sistema.&lt;/p&gt;

&lt;p&gt;Pensaba en eliminarla, pero después de investigar más, decidí más bien expandir la interfaz. Mantener &lt;code&gt;IView&lt;/code&gt; como una clase abstracta significa que, si alguien quiere escribir su propia Vista para un sistema operativo que FTXUI no soporta, puede implementar la interfaz y conectarla sin tocar el Presentador ni el Modelo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;IView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ViewState&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getTerminalSize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;showMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una explicación rápida: &lt;code&gt;run()&lt;/code&gt; inicia el bucle de eventos y recibe un callback para manejar la entrada; esto es lo que el Presentador llama en su bucle principal. &lt;code&gt;render()&lt;/code&gt; toma el &lt;code&gt;ViewState&lt;/code&gt; que vimos en la Parte 2 y lo dibuja en la pantalla. &lt;code&gt;exit()&lt;/code&gt; y &lt;code&gt;getTerminalSize()&lt;/code&gt; hacen lo que dicen, y &lt;code&gt;showMessage()&lt;/code&gt; es la forma en que el Presentador muestra advertencias como el aviso de "cambios sin guardar" del flujo de salida.&lt;/p&gt;

&lt;h2&gt;
  
  
  Renderizando la interfaz
&lt;/h2&gt;

&lt;p&gt;Como mostramos en la Parte 2, la estructura &lt;code&gt;ViewState&lt;/code&gt; define el contrato de cómo el Presentador envía datos a la Vista:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;ViewState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Content&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;visibleText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Text currently visible in viewport&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cursorPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Linear position of cursor in visibleText&lt;/span&gt;

    &lt;span class="c1"&gt;// Status bar information&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;wordCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Total word count (primary metric for writers)&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Current file name (or "Untitled")&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isDirty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Unsaved changes indicator&lt;/span&gt;

    &lt;span class="c1"&gt;// UI state&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Temporary status message (e.g., "Saved", "Error: ...")&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;showHelp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Whether to show help overlay&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hay tres grandes tipos de datos en el contrato de &lt;code&gt;ViewState&lt;/code&gt;: &lt;strong&gt;Contenido&lt;/strong&gt; , que nos dice la &lt;code&gt;cursorPosition&lt;/code&gt; y el &lt;code&gt;visibleText&lt;/code&gt;. &lt;strong&gt;Información de la barra de estado&lt;/strong&gt; : &lt;code&gt;wordCount&lt;/code&gt;, &lt;code&gt;filename&lt;/code&gt; e &lt;code&gt;isDirty&lt;/code&gt;, que indica si el archivo está sin guardar. Por último, el &lt;strong&gt;estado de la interfaz&lt;/strong&gt; : &lt;code&gt;statusMessage&lt;/code&gt; y &lt;code&gt;showHelp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Uno de los mayores retos fue cómo lidiar con el cursor, cómo moverlo y poder agregar más al búfer cuando el cursor se mueve. Una de las soluciones más simples es dividir el &lt;code&gt;visibleText&lt;/code&gt; en tres componentes: beforeCursor, cursor y afterCursor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Element&lt;/span&gt; &lt;span class="n"&gt;FtxuiView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;renderEditor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibleText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursorPosition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Character at cursor with cyan background&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cursorChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursorChar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;bgcolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cyan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Black&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hbox&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El cursor se resalta con un fondo cian para que el usuario pueda ver dónde está en el texto. Este reto existía tanto en ncurses como en FTXUI; ninguna biblioteca te da un cursor integrado para un editor de texto personalizado. Tienes que simularlo tú mismo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traduciendo la entrada del teclado
&lt;/h2&gt;

&lt;p&gt;La Vista también es responsable de traducir los eventos crudos del teclado a las estructuras &lt;code&gt;InputEvent&lt;/code&gt; que el Presentador entiende. FTXUI tiene su propio sistema de eventos, así que la Vista necesita una capa de traducción entre lo que FTXUI nos da y lo que nuestro Presentador espera. El método &lt;code&gt;translateEvent()&lt;/code&gt; se encarga de este mapeo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="n"&gt;FtxuiView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;translateEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ArrowLeft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ARROW_LEFT&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ArrowRight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ARROW_RIGHT&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Backspace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BACKSPACE&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ENTER&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CtrlQ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_Q&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CtrlS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_S&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Printable character&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_character&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="n"&gt;ie&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CHARACTER&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;ie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El patrón es sencillo: cada evento de FTXUI se mapea a uno de nuestros tipos &lt;code&gt;InputEvent&lt;/code&gt;. Si es un carácter imprimible, extraemos el valor del carácter. Si no reconocemos el evento, devuelve &lt;code&gt;UNKNOWN&lt;/code&gt; y se ignora. Esta es la misma capa de traducción que habría que reimplementar si alguien escribiera una nueva Vista usando una biblioteca distinta, otro beneficio de la interfaz &lt;code&gt;IView&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que la Vista puede hacer hasta ahora
&lt;/h2&gt;

&lt;p&gt;La Vista funciona bien para el estado actual del proyecto. Renderiza el texto conforme el usuario escribe, muestra la barra de estado con el nombre del archivo, el conteo de palabras y el indicador de cambios sin guardar, captura todos los eventos de entrada y los enruta al Presentador, y alterna la superposición de ayuda con F1. No es perfecta: los saltos de línea todavía no se renderizan y las teclas de flecha no pueden navegar del todo hacia arriba y hacia abajo por el texto. Pero para la Fase 1, cumple su trabajo como la capa pasiva del patrón MVP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cerrando la serie
&lt;/h2&gt;

&lt;p&gt;Con esto cerramos la serie de tres partes sobre la construcción de la Fase 1 de wordNebula. A lo largo de estas entradas fuimos desde el origen del proyecto y por qué elegí Modelo-Vista-Presentador, pasando por el Modelo con el GapBuffer y la interfaz IBuffer, el Presentador que orquesta todo y gestiona el estado, y finalmente la Vista que lo renderiza todo en la terminal.&lt;/p&gt;

&lt;p&gt;Construir cada capa de forma independiente y conectarlas mediante contratos como &lt;code&gt;IBuffer&lt;/code&gt; e &lt;code&gt;IView&lt;/code&gt; hizo que el proyecto fuera manejable. Podía trabajar en una pieza a la vez sin preocuparme por romper las demás. Esa separación es justamente el propósito de MVP, y valió la pena.&lt;/p&gt;

&lt;p&gt;La Fase 1 está completa pero lejos de estar terminada. Hay más por construir, pero por ahora me alejo para enfocarme en otros proyectos. Si quieres explorar el código, échale un vistazo al proyecto en &lt;a href="https://github.com/ileanmjr88/wordNebula" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>cpp</category>
      <category>terminal</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Construyendo un editor de texto de terminal: el Presentador (Parte 2)</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/construyendo-un-editor-de-texto-de-terminal-el-presentador-parte-2-2lc8</link>
      <guid>https://dev.to/elcapitan/construyendo-un-editor-de-texto-de-terminal-el-presentador-parte-2-2lc8</guid>
      <description>&lt;h2&gt;
  
  
  Resumen
&lt;/h2&gt;

&lt;p&gt;En la &lt;a href="https://dev.to/es/blog/construyendo-un-editor-de-texto-de-terminal-el-modelo-parte-1/"&gt;Parte 1&lt;/a&gt; hablé del origen de este proyecto y de las primeras decisiones de arquitectura. Después de elegir Modelo-Vista-Presentador, dividir el proyecto en Modelo, Vista y Presentador se sintió como el camino natural. Abordamos el &lt;code&gt;GapBuffer&lt;/code&gt; con &lt;code&gt;IBuffer&lt;/code&gt; como el contrato para todas las futuras implementaciones de búfer. Con el Modelo sosteniéndose por sí solo, podemos pasar al Presentador.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es el Presentador?
&lt;/h2&gt;

&lt;p&gt;Ya insinuamos en la Parte 1 que el Presentador es el orquestador: el que maneja toda la lógica de "negocio" de la aplicación. Es responsable de mantener actualizados tanto el Modelo como la Vista. El Presentador toma la entrada del usuario desde la Vista y se la pasa al Modelo, luego obtiene los datos actualizados del Modelo y los envía de vuelta a la Vista para renderizarlos. Toda la comunicación entre ambos pasa por el Presentador.&lt;/p&gt;

&lt;p&gt;Tomemos el caso de uso más común: un usuario presionando una sola tecla. En realidad estará escribiendo varias en sucesión, y este proceso se repite constantemente en la implementación actual.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Paso&lt;/th&gt;
&lt;th&gt;De&lt;/th&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;Qué ocurre&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Usuario (actor)&lt;/td&gt;
&lt;td&gt;Vista&lt;/td&gt;
&lt;td&gt;Presiona una tecla&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Vista&lt;/td&gt;
&lt;td&gt;Presentador&lt;/td&gt;
&lt;td&gt;Pasa el evento de tecla&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Presentador&lt;/td&gt;
&lt;td&gt;Modelo&lt;/td&gt;
&lt;td&gt;Llama a insertChar()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Presentador&lt;/td&gt;
&lt;td&gt;Modelo&lt;/td&gt;
&lt;td&gt;Solicita el texto actualizado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Presentador&lt;/td&gt;
&lt;td&gt;Vista&lt;/td&gt;
&lt;td&gt;Envía el texto para renderizar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Vemos el gran papel que cumple el Presentador y exploraremos cómo creamos contratos entre los componentes para asegurar que los datos se pasen correctamente. Para wordNebula, esto también significa que el Presentador será donde viva la lógica futura.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conectándolo todo
&lt;/h2&gt;

&lt;p&gt;Como este proyecto usa C++ moderno, quise aprovechar los smart pointers y apoyarme en Resource Acquisition Is Initialization (RAII). De esta forma no tenemos que gestionar la memoria de forma explícita; es la práctica estándar en C++ moderno para cualquier proyecto de sistemas.&lt;/p&gt;

&lt;p&gt;Al empezar a conectar los distintos componentes, incluyendo la Vista simulada de las pruebas originales que hice antes de abordar el proyecto completo, tuve que tomar algunas decisiones sobre la propiedad (ownership). Así se crean los tres componentes en &lt;code&gt;main&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;presenter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WNebulaModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FtxuiView&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;presenter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;presenter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Los tres se crean como &lt;code&gt;shared_ptr&lt;/code&gt;, pero dentro del Presentador se almacenan de forma diferente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;weak_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IView&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WNebulaModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El Modelo se queda como &lt;code&gt;shared_ptr&lt;/code&gt; porque el Presentador necesita ser su dueño: son los datos centrales y deben existir mientras la aplicación esté corriendo. La Vista se almacena como &lt;code&gt;weak_ptr&lt;/code&gt; para romper una dependencia circular. El Presentador necesita la Vista para enviar actualizaciones, y la Vista necesita al Presentador para enviar la entrada. Si ambos guardaran un &lt;code&gt;shared_ptr&lt;/code&gt; el uno del otro, ninguno se liberaría jamás.&lt;/p&gt;

&lt;p&gt;La contrapartida de &lt;code&gt;weak_ptr&lt;/code&gt; es que cada vez que el Presentador necesita actualizar la Vista, tiene que convertirla temporalmente de vuelta a un &lt;code&gt;shared_ptr&lt;/code&gt; llamando a &lt;code&gt;lock()&lt;/code&gt;. Si la Vista todavía existe, &lt;code&gt;lock()&lt;/code&gt; nos da un &lt;code&gt;shared_ptr&lt;/code&gt; válido con el cual trabajar. Si no, sabemos que la Vista ya no está y podemos manejarlo de forma elegante. Esto ocurre dentro de &lt;code&gt;updateView()&lt;/code&gt; cada vez que el Presentador necesita renderizar datos nuevos en la pantalla.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manejando la entrada del usuario
&lt;/h2&gt;

&lt;p&gt;En las pruebas exploratorias solo me preocupaba por la entrada de caracteres; era únicamente para comprobar si entendía la arquitectura. Para la implementación real, necesitaba realmente contemplar las teclas especiales: &lt;code&gt;CTRL&lt;/code&gt;, &lt;code&gt;BACKSPACE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;ARROW KEYS&lt;/code&gt;, &lt;code&gt;ENTER&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;Para manejar esto de forma limpia, creé una estructura &lt;code&gt;InputEvent&lt;/code&gt; que traduce la entrada cruda del teclado en eventos semánticos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;InputEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;CHARACTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ARROW_LEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARROW_RIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARROW_UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARROW_DOWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CTRL_LEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_RIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_DOWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;BACKSPACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DELETE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ENTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CTRL_S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_Q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ESCAPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// ... other types&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'\0'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Only valid for Type::CHARACTER&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De esta forma al Presentador no le importan los códigos de escape de la terminal: solo recibe un tipo de evento y actúa en consecuencia. El método &lt;code&gt;handleInput&lt;/code&gt; enruta cada evento a la operación correcta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;handleInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CHARACTER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BACKSPACE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_LEFT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onCtrlLeft&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_Q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other cases&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada operación sigue el mismo patrón: delegar en el Modelo, marcar como modificado (dirty) si hace falta y actualizar la Vista:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;onInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;insertChar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;isDirty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;updateView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gestión del viewport
&lt;/h2&gt;

&lt;p&gt;Una de las cosas que aprendí en el camino, o sobre la que tuve que pensar de verdad, fue el viewport. Hasta ese momento, las pruebas iniciales consistían en que yo escribía frases sencillas, no entradas de blog completas ni texto de formato más largo. El área de la terminal es finita, así que determinar qué porción del búfer mostrar y cómo presentarla fue todo un reto.&lt;/p&gt;

&lt;p&gt;El Presentador resuelve esto construyendo un &lt;code&gt;ViewState&lt;/code&gt;: una estructura que empaqueta todo lo que la Vista necesita para renderizar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;ViewState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;visibleText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cursorPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;wordCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isDirty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;showHelp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada vez que el Presentador actualiza la Vista, obtiene el texto actual y la posición del cursor del Modelo y los envía como un &lt;code&gt;ViewState&lt;/code&gt;. La Vista no sabe nada del búfer: solo renderiza cualquier estado que reciba. Aquí está el método &lt;code&gt;updateView()&lt;/code&gt; que construye y envía ese estado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;updateView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ViewState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibleText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursorPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getCursorPosition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wordCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getWordCount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentFilePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Untitled"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currentFilePath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isDirty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDirty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;showHelp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;showHelp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fíjate en el &lt;code&gt;view.lock()&lt;/code&gt;: es la conversión de &lt;code&gt;weak_ptr&lt;/code&gt; de la que hablamos en la sección de conexión. Cada vez que el Presentador necesita actualizar la Vista, comprueba que la Vista todavía exista antes de enviar datos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gestión del estado
&lt;/h2&gt;

&lt;p&gt;Para gestionar el estado en esta iteración del proyecto lo mantuve simple. El Presentador rastrea unas cuantas banderas booleanas: &lt;code&gt;isDirty&lt;/code&gt; para saber cuándo el archivo no se ha guardado, &lt;code&gt;isRunning&lt;/code&gt; para controlar el bucle principal, &lt;code&gt;showHelp&lt;/code&gt; para alternar la superposición de ayuda y &lt;code&gt;exitWarningShown&lt;/code&gt; para la confirmación de salida.&lt;/p&gt;

&lt;p&gt;El más interesante es el flujo de salida. Si tienes cambios sin guardar y presionas Ctrl+Q, el Presentador te avisa y te hace presionarlo de nuevo para salir de verdad:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;onExit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isDirty&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;exitWarningShown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;exitWarningShown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;showMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unsaved changes! Press Ctrl+Q again to quit."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;isRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lo que el Presentador puede hacer hasta ahora
&lt;/h2&gt;

&lt;p&gt;En esta etapa, el Presentador recibe todos los eventos de entrada y determina si se trata de una tecla especial o un carácter, gestiona por completo el estado de la aplicación y del documento, y mantiene el Modelo y la Vista sincronizados. Es el pegamento entre ambos y, gracias a MVP, se puede probar de forma independiente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que sigue
&lt;/h2&gt;

&lt;p&gt;En la Parte 3 hablaré de por qué elegí FTXUI en lugar de ncurses y de lo que ofrece de fábrica que hizo más fácil construir la capa de la Vista. Puedes ver el proyecto en &lt;a href="https://github.com/ileanmjr88/wordNebula" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>cpp</category>
      <category>datastructures</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Building a Terminal Text Editor: The Presenter (Part 2)</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/building-a-terminal-text-editor-the-presenter-part-2-2h08</link>
      <guid>https://dev.to/elcapitan/building-a-terminal-text-editor-the-presenter-part-2-2h08</guid>
      <description>&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://www.ilean.me/blog/building-a-terminal-text-editor-the-model-part-1/" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;, I talked about the origin of this project and the early architectural decisions. After picking Model-View-Presenter, breaking up the project into Model, View, and Presenter felt like the natural path forward. We tackled the &lt;code&gt;GapBuffer&lt;/code&gt; with &lt;code&gt;IBuffer&lt;/code&gt; as the contract for all future buffer implementations. With the Model standing on its own, we can move on to the Presenter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Presenter?
&lt;/h2&gt;

&lt;p&gt;We already hinted in Part 1 that the Presenter is the orchestrator — the one that handles all the application's "business" logic. It is responsible for keeping both the Model and the View up to date. The Presenter takes user input from the View and passes it to the Model, then pulls the updated data from the Model and sends it back to the View for rendering. All communication between the two goes through the Presenter.&lt;/p&gt;

&lt;p&gt;Let's take the most common use case of a user pressing a single key stroke. In reality, they will be typing several in succession, and this process gets repeated constantly in the current implementation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;From&lt;/th&gt;
&lt;th&gt;To&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;User (actor)&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;td&gt;Presses key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;td&gt;Presenter&lt;/td&gt;
&lt;td&gt;Passes the key event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Presenter&lt;/td&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Calls insertChar()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Presenter&lt;/td&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Requests updated text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Presenter&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;td&gt;Sends text to render&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We see the big role that the Presenter fills and we will explore how we created contracts between the components to ensure that data is passed correctly. For wordNebula, this also means the Presenter will be where future logic lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring It Together
&lt;/h2&gt;

&lt;p&gt;Since this project is using modern C++ I wanted to take advantage of smart pointers and leverage Resource Acquisition Is Initialization (RAII). This way we don't have to explicitly manage memory — this is standard practice in modern C++ for any systems project.&lt;/p&gt;

&lt;p&gt;When starting to wire up the different components, including the mock View from the original testing I did before tackling the full project, I had to make some decisions on ownership. Here is how the three components get created in &lt;code&gt;main&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;presenter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WNebulaModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FtxuiView&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;presenter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;presenter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three are created as &lt;code&gt;shared_ptr&lt;/code&gt;, but inside the Presenter they are stored differently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;weak_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IView&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WNebulaModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Model stays as a &lt;code&gt;shared_ptr&lt;/code&gt; because the Presenter needs to own it — it is the core data and must exist as long as the application is running. The View is stored as a &lt;code&gt;weak_ptr&lt;/code&gt; to break a circular dependency. The Presenter needs the View to push updates, and the View needs the Presenter to send input. If both held &lt;code&gt;shared_ptr&lt;/code&gt; to each other, neither would ever get cleaned up.&lt;/p&gt;

&lt;p&gt;The trade-off with &lt;code&gt;weak_ptr&lt;/code&gt; is that every time the Presenter needs to update the View, it has to convert it back to a &lt;code&gt;shared_ptr&lt;/code&gt; temporarily by calling &lt;code&gt;lock()&lt;/code&gt;. If the View still exists, &lt;code&gt;lock()&lt;/code&gt; gives us a valid &lt;code&gt;shared_ptr&lt;/code&gt; to work with. If it doesn't, we know the View is gone and can handle it gracefully. This happens inside &lt;code&gt;updateView()&lt;/code&gt; every time the Presenter needs to render new data to the screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling User Input
&lt;/h2&gt;

&lt;p&gt;In the exploratory testing, I only worried about character user input — it was just to test if I understood the architecture. For the real implementation, I needed to actually account for special keys: &lt;code&gt;CTRL&lt;/code&gt;, &lt;code&gt;BACKSPACE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;ARROW KEYS&lt;/code&gt;, &lt;code&gt;ENTER&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;To handle this cleanly, I created an &lt;code&gt;InputEvent&lt;/code&gt; struct that translates raw keyboard input into semantic events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;InputEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;CHARACTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ARROW_LEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARROW_RIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARROW_UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARROW_DOWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CTRL_LEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_RIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_DOWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;BACKSPACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DELETE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ENTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CTRL_S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_Q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ESCAPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// ... other types&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'\0'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Only valid for Type::CHARACTER&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way the Presenter doesn't care about terminal escape codes — it just receives an event type and acts on it. The &lt;code&gt;handleInput&lt;/code&gt; method routes each event to the right operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;handleInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CHARACTER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BACKSPACE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_LEFT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onCtrlLeft&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_Q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other cases&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each operation follows the same pattern — delegate to the Model, mark dirty if needed, update the View:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;onInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;insertChar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;isDirty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;updateView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Viewport Management
&lt;/h2&gt;

&lt;p&gt;One of the things I learned along the way, or had to really think about, was the viewport. So far, the initial testing was me writing simple sentences, not full blog entries or longer format text. The area of the terminal is finite, so determining what portion of the buffer to display and how to present it was a challenge.&lt;/p&gt;

&lt;p&gt;The Presenter solves this by building a &lt;code&gt;ViewState&lt;/code&gt; — a struct that packages everything the View needs to render:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;ViewState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;visibleText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cursorPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;wordCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isDirty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;showHelp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time the Presenter updates the View, it pulls the current text and cursor position from the Model and sends it as a &lt;code&gt;ViewState&lt;/code&gt;. The View doesn't know anything about the buffer — it just renders whatever state it receives. Here is the &lt;code&gt;updateView()&lt;/code&gt; method that builds and sends that state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;updateView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ViewState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibleText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursorPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getCursorPosition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wordCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getWordCount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentFilePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Untitled"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currentFilePath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isDirty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDirty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;showHelp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;showHelp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;view.lock()&lt;/code&gt; — this is the &lt;code&gt;weak_ptr&lt;/code&gt; conversion we talked about in the Wiring section. Every time the Presenter needs to update the View, it checks that the View still exists before sending data.&lt;/p&gt;

&lt;h2&gt;
  
  
  State Management
&lt;/h2&gt;

&lt;p&gt;For managing state in this iteration of the project I kept it simple. The Presenter tracks a few boolean flags — &lt;code&gt;isDirty&lt;/code&gt; to know when the file hasn't been saved, &lt;code&gt;isRunning&lt;/code&gt; to control the main loop, &lt;code&gt;showHelp&lt;/code&gt; to toggle the help overlay, and &lt;code&gt;exitWarningShown&lt;/code&gt; for the quit confirmation.&lt;/p&gt;

&lt;p&gt;The most interesting one is the exit flow. If you have unsaved changes and press Ctrl+Q, the Presenter warns you and makes you press it again to actually quit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;onExit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isDirty&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;exitWarningShown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;exitWarningShown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;showMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unsaved changes! Press Ctrl+Q again to quit."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;isRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What the Presenter Can Do So Far
&lt;/h2&gt;

&lt;p&gt;At this stage, the Presenter takes in all input events and determines if it is a special key or character, fully manages the state of the application and document, and keeps the Model and View in sync. It is the glue between the two, and because of MVP, it can be tested independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In Part 3, I will talk about why I picked FTXUI instead of ncurses and what it offers out of the box that made the View layer easier to build. You can check out the project on &lt;a href="https://github.com/ileanmjr88/wordNebula" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>terminal</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
