Ahhh ya veo; es justo como los lenguajes orientados a objetos manejan vectores simples de objetos: al intercambiar de posición elementos del mismo vector, sólo intercambias sus referencias (punteros) y no los objetos en sí. Así lo tengo aprendido.
Pues sí, mover punteros es computacionalmente más sencillo que mover bloques enteros de datos.
noter:
El archivo que creo tiene tres partes:
- Una cabecera de tamaño fijo en la que se indican el tamaño de la estructura o registro, número de registros válidos, número de registros borrados (físicamente siguen estando en la tabla, pero su espacio será utilizado en primer lugar para nuevas inserciones), y posición del archivo en la que comienzan los datos propiamente dichos, pues antes de ellos está la siguiente sección.
- Índice ordenado según el criterio que hemos proporcionado. Aquí es donde realmente trabajan las operaciones. Es una lista consecutiva (y ordenada según el criterio de índice) de punteros (unsigned long) a los registros propiamente dichos en sección de datos. Es decir, el primer unsigned long apunta al primer registro según el orden (aunque el registro esté físicamente al final del archivo), el segundo al segundo, y así sucesivamente hasta el número de registros válidos indicado en la cabecera. A continuación están tantos punteros a registros borrados como indica la cabecera.
- Finalmente están los registros propiamente dichos, sin orden específico.
A diferencia de tu implementación, yo tenía pensado agregar una sección opcional y dos obligatorias en la cabecera:
Opcional: una descripción de hasta 255 campos, 20 caracteres cada una. Si tu implementación no tiene forma directa de determinar la composición de un registro, entonces esta sección sería meramente de etiquetado, sin verificación y la cantidad determinada en tiempo de creación del archivo (como parámetro de una función).
Es solo una idea, podría expanderse el límite si 20 caracteres no son suficientes; o incluso agregar datos extra como el tipo de dato que se supone que representa.
Obligatorio 1: un "número mágico" que identifique este tipo de archivo. No solo serviría como primer paso de validación; sino que incluso podría ser útil para los software recuperadores de archivos borrados que tengan la opción de insertar un nuevo patrón de reconocimiento de cabecera.
Obligatorio 2: no tan obligatorio, pero se me ocurrió que se podría agregar un identificador de versión. Si alguna eventual revisión implicara modificar la estructura de la cabecera, este identificador inmediatamente nos diría si esta debe leerse de la forma nueva, o de alguna de las formas antiguas (si la retrocompatibilidad no es una opción, entonces sirve para detectar un archivo "incompatible").
Y luego se me vino a la mente que si agregarle una bandera de "histórico"; si está activa, entonces la base de datos se finaliza y a partir de ahí considera histórica; por lo tanto, de solo lectura.
Si los AVR fueran lo suficientemente potentes, también agregaría sumas de verificación (checksums) para robustecer más el formato del archivo.
Pero bueno, al ser una implementación cuya utilidad aún se cuestiona, todavía hay que pensar detenidamente qué vale la pena agregar y qué no.
noter:
Para buscar un determinado índice, se cargarían en una estructura de datos busqueda los valores de índice buscados
Si el tamaño del registro se declara excesivo, esto no sería posible cuando estamos apretados en RAM.
noter:
Lo único a tener en cuenta es que las modificaciones no afecten al índice (posición), en cuyo caso opté por devolver error si coincide con otro índice existente
Explícate bien esa parte, que de primera impresión me pareció contradictorio.
Si modifico un registro ya existente, entonces podría deliberadamente arruinar el orden (a menos que se esté aplicando un algoritmo de ordenamiento en cada intento). Para evitar la situación anterior, se lanza un error; por lo tanto... ¿no se pueden modificar registros existentes?
noter:
Y ahora vienen las operaciones que pueden llegar a gastar más tiempo, ya que requieren desplazamiento de datos. La única ventaja es que sólo se desplazan los datos de índice (unsigned long), no los datos en sí, que pueden ser bastante más pesados. Por ello decía que la ventaja de este sistema la proporciona cuando el tamaño de registro es "grande". Para un listado de números de teléfono, que se pueden guardar como unsigned long, no merece la pena; pero para número de teléfono+nombre, ya empieza a ser ventajoso, si vamos a "atacar" la tabla de forma indexada.
Y supongo que, al ser las operaciones con tarjeta SD bajo buffer, desplazar punteros tampoco se tarda mucho (se pueden desplazar hasta 127 sin tener que mandar a escribir el bloque completo).
noter:
Así que hay que reescribir los índices desde la posición de borrado hasta el final de los índices; si el índice a borrar es el primero, deberemos reescribir tooodo el índice (4bytes*número de registros). Aún así, es mejor que reescribir todos los datos, ¿no?.
Es lo que acabo de decir...
noter:
Para agregar un nuevo registro, primero buscamos la posición en la que debe ir su índice. A continuación, si hay punteros a registros borrados, aprovechamos el primero, sobreescribimos sus datos
Este siempre está después del último puntero válido del vector, ¿o no?. Si están mezclados, ¿cómo vas a determinar un puntero inválido?
noter:
Se presenta un problemazo: ¿qué pasa si no hay espacio para el nuevo índice, porque comenzaría a machacar la sección de datos? Pues que hay que mover el primer bloque de datos al final para hacer hueco a nuevos índices (y actualizar su nueva posición en el índice correspondiente y los datos de cabecera para indicar dónde empiezan ahora los datos).
Entonces haces crecer el archivo según la demanda; en vez de fijar su tamaño inicialmente.
En ese caso se me ocurre que la cabecera acabe en un puntero a la tabla de índices; así lo podrás ubicar dinámicamente junto con los demás registros. Es la misma técnica que utiliza NTFS (o FAT con los subdirectorios de la raíz): tiene una tabla con los metadatos de todos los archivos, la "Master File Table" o MFT, la cual en sí también es un archivo.
Para esto tendrías que decidir si los registros deben tener una longitud múltiplo de 4, o la tabla deber ser múltiplo del tamaño de los registros; ya que, así como la MFT es un archivo en NTFS, la tabla de índices se tendrá que almacenar como uno o múltiples registros.
Conviene nunca fragmentarla; de lo contrario la tabla pasará a ser una lista enlazada.