Mi primera reacción sería usar una memoria RAM externa grande para mantener los datos que se están editando. Pero en lugar de hacer una copia de imagen directa, asignaría previamente bloques de RAM en una lista vinculada. Cada bloque tendría un puntero hacia adelante, un puntero hacia atrás, un tamaño utilizado y un búfer de datos de tamaño fijo.
Haz de cada bloque una potencia de dos bytes de tamaño. Eso significa que se sabe que los pocos bits de dirección bajos para cada bloque son 0, por lo que no es necesario almacenarlos. Los punteros hacia adelante y hacia atrás podrían ser de 16 bits y la longitud utilizada de 1 byte, para un total de 5 bytes de sobrecarga por bloque. Con bloques de 32 bytes, por ejemplo, esto representa un 16% de sobrecarga. 1 Mbyte de RAM daría 885 kBytes de almacenamiento de datos reales, mucho más que los 300 kBytes que solicitó.
En efecto, este esquema crea una memoria lineal que puede tener datos insertados o eliminados en lugares arbitrarios con poca sobrecarga. Al agregar o eliminar, no tiene que mirar más allá de los bloques adyacentes en la cadena, por lo que puede hacerse de manera instantánea en tiempo humano.
Mantiene dos cadenas, una para los bloques que contienen los datos y otra para los bloques no utilizados. El tipo correcto de edición en los lugares correctos podría ocasionar tanta sobrecarga de fragmentación como para hacer que la memoria se vea mucho más pequeña y, en última instancia, no pueda contener 300 kBytes. Sin embargo, es improbable que se produzca dicha edición específica, y siempre puede hacer simples combinaciones locales (si dos bloques adyacentes contienen menos datos que un bloque, luego fusione los dos y vuelva a colocar uno en la lista gratuita), que la mayoría de las veces mantendrá fragmentación bastante bien abajo. La fragmentación en realidad no importa hasta que necesite un bloque libre y no haya uno. Cuando eso sucede, haces una desfragmentación y el usuario tiene que esperar unos segundos, pero esto será muy raro. Una buena estrategia sería hacer una desfragmentación automática en segundo plano durante los inevitables largos períodos de tiempo (desde el punto de vista del procesador) donde el usuario no está haciendo nada. Con esta estrategia, creo que es muy improbable que alguna vez te quedes sin bloques libres y debas desfragmentar mientras el usuario espera.
Este esquema es fácil de administrar incluso en un pequeño microcontrolador, la respuesta del usuario a las operaciones de edición es rápida y solo se accede a la tarjeta SD al inicio y al final de las sesiones de edición.
Añadido:
Estaba considerando la fragmentación más después de escribir la publicación, y creo que se puede mostrar que siempre que realice una desfragmentación local simple en cualquier operación de inserción o eliminación, nunca perderá más de la mitad de la memoria disponible. Utilizando de nuevo el ejemplo de la memoria de 1 Mbyte con bloques de 32 bytes, tiene garantizados al menos 442 kBytes de almacenamiento.
Después de una eliminación, fusiona el bloque que tenía un byte eliminado con uno de sus vecinos si los dos juntos ahora contienen un bloque de datos o menos. En un agregado de bytes, los datos del bloque actual se transfieren a un vecino para hacer espacio en lugar de tomar un nuevo bloque a menos que ambos vecinos (y, por supuesto, el bloque actual) estén completamente llenos.
Todas estas operaciones nunca envuelven más de 3 bloques, por lo que son instantáneas en el tiempo humano. Si puede vivir con la mitad de la memoria disponible sin usar, entonces no necesita hacer nada más. Sin embargo, no hay nada de malo en realizar la desfragmentación de fondo cuando no ocurre nada más.