Hoy voy a empezar a hablar de la librería CSharpUtils que tengo en googlecode.

Hace un par de meses volví a meterme con C# después de varios años. Y la verdad es que estoy muy contento con C#, .NET y el espléndido trabajo de mono-project que permite ejecutar .NET 4.0 tanto en Linux como en MAC incluyendo aplicaciones GUI y aplicaciones de servidor con ASP.NET y FastCGI.

Comentar también que hay un compilador de PHP que genera código .NET que permite ejecutar PHP muchísimo más rápido utilizando el JIT de .NET: phalanger. Aunque el hecho de que en PHP no haya tipado estricto impide bastantes optimizaciones.

Ahora sí, voy a hablar de algunas utilidades que he ido añadiendo a CSharpUtils.
Para empezar, y siendo algo que echo siempre mucho en falta del grandioso lenguaje D y phobos, es parte de su API, muy sencilla y práctica. Y concretamente una clase que me parece extremadamente útil y que no está implementada en prácticamente ningún lenguaje popular. Me refiero a la clase SliceStream.

SliceStream:

SliceStream es una clase que permite trabajar con un segmento de un Stream Seekable como si fuese un Stream en sí mismo y sin alterar el puntero original. Es extremadamente útil cuando trabajamos con archivos binarios que tienen varias secciones. Generalmente suele haber punteros relativos que hacen referencia a partes de alguna o varias de esas secciones.

La opción tradicional sería:

  • Al empezar
  • Guardarnos el offset de cada sección
  • Por cada acceso a referencia
  • Guardarnos el cursor actual del archivo
  • Cambiar el cursor del archivo a offset de inicio de sección + la posición relativa referenciada
  • Obtener datos referenciados
  • Restaurar el cursor anterior del archivo
  • Comentarios
  • Es posible “salirse” de la sección sin darnos cuenta ya que tenemos acceso al archivo entero.

La opción con SliceStream sería:

  • Al empezar
  • Crear un SliceStream a partir del Stream original y de los offsets/tamaño de cada sección
  • Por cada acceso a referencia
  • Cambiamos el cursor del SliceStream a la posición relativa referenciada
  • Obtener datos referenciados con este SliceStream
  • Comentarios
  • Es imposible “salirse” de la sección, ya que la hemos delimitado y el SliceStream solo nos deja acceder al segmento delimitado.
  • SliceStream se encarga de hacer un lock del Stream original y de cambiar el cursor del archivo y restituirlo tras cada operación de lectura/escritura.

SliceStream también es útil para archivos tipo Stream en el que se van leyendo poco a poco secciones determinadas por Streams y Substreams. Por ejemplo en archivos multimedia: vídeos, audio, etc.

También sirve para pasar referencias de un Stream garantizando que será inmutable y no se alterará el cursor del Stream original.

Para ver ejemplos de uso me remito al unittesting.

SpaceAssigner1D:

SpaceAssigner1D es una clase muy específica que trata de resolver un problema muy común que se da en el romhacking. Aunque también hay otros usos, por supuesto. El problema que resuelve es el siguiente:

Tenemos un archivo con punteros a texto y queremos modificar esos textos que generalmente estarán en inglés o en japonés. En español los textos suelen ocupar más que en inglés o el japonés, así que generalmente no cabrán los textos traducidos en el espacio original. Habrán algunos textos que ocuparán más, otros menos.  Los textos que aparezcan juntos se pueden utilizar para crear “segmentos” para usar más grandes. En ocasiones también hay zonas “dummy” en el archivo que podemos utilizar como “huecos” para más texto.

La solución a este problema pasa siempre por reasignar los punteros. Utilizando el espacio restante al quitar los textos originales creamos segmentos, y en esos segmentos vamos colocando los textos traducidos “donde quepan”, reutilizando los textos repetidos siempre que vayan a ser inmutables y actualizando los punteros con las nuevas posiciones.

Este problema se puede resolver con gran facilidad usando SpaceAssigner1D y las clases utilitarias asociadas que creé: SpaceAssigner1DUniqueAllocator, SpaceAssigner1DUniqueAllocatorStream, que permiten reutilizar textos e incluso escribir directamente los textos y los punteros sobre un Stream.

Ejemplos en el unittesting.

this.SpaceAssigner1D = new SpaceAssigner1D();  
SpaceAssigner1D.AddAvailable(new SpaceAssigner1D.Space(-7, 0));  
SpaceAssigner1D.AddAvailable(new SpaceAssigner1D.Space(1, 10));  
var Space3 = SpaceAssigner1D.Allocate(9);  
var Space1 = SpaceAssigner1D.Allocate(3);  
var Space2 = SpaceAssigner1D.Allocate(4);  

Assert.AreEqual(new SpaceAssigner1D.Space(-7, -4), Space1);  
Assert.AreEqual(new SpaceAssigner1D.Space(-4, 0), Space2);  
Assert.AreEqual(new SpaceAssigner1D.Space(1, 10), Space3);  
Assert.AreEqual("SpaceAssigner1D()", SpaceAssigner1D.ToString());