Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Objetos String_t #7

Open
6 tasks
martinribelotta opened this issue Jan 5, 2017 · 8 comments
Open
6 tasks

Objetos String_t #7

martinribelotta opened this issue Jan 5, 2017 · 8 comments

Comments

@martinribelotta
Copy link
Contributor

martinribelotta commented Jan 5, 2017

Abro este ISSUE como mejora para discutir la creación de un api String_t en C

Criterios de diseño

  • Full estatico
  • Igual de eficiente que las librerias <string.h> (requiere test para corroborar esto)
  • Ocultar el uso de punteros
  • Compatible con los string NULL terminated?
  • Permitir strings sin null pointer termination
  • Permitir substrings sin overhead de alloc

Ideas

Head/Tail

typedef struct {
    char *head;
    char *tail;
} String_t

String_t s1 = StringNew("Hola mundo");
String_t s2 = StringSubString(s1, "la mu");
String_t s3 = StringTrim(StringNew("    linea de comando     "));
String_t s4 = StringNew(40);

while (!StringEqual(ReadLine(s4, StringLen(s4)), "quit")
{
  switch(StringAt(StringTrim(s4), 0)) {
  case '+':
    {
       String_t tokens[2];
       int n = StringSplit(StringLeft(s4, 1, StringLen(s4) - 1), tokens, ' ', ARRAY_LEN(tokens));
       if (n != ARRAY_LEN(tokens)) {
            ERROR(...)
       } else {
            int a = StringToInt(tokens[0], RADIX_10);
            int b = StringToInt(tokens[0], RADIX_10);
            int c = a + b;
            String_t sc = StringFromInt(
            UARTSend(UART1, StringAddress(sc), StringLen(sc));
       }
    }
    break,
  }
}

String_t s5 = StringLeft(s1, 3);
String_t s6 = StringRigth(s5, 2);
String_t s7 = StringMid(s3, 3, 5);
String_t s8 = StringLeft(s1, -4); // -> undo
@epernia
Copy link
Contributor

epernia commented Jan 6, 2017

Muy bueno!!

Una duda, los tipos de datos vamos a diferneciar entre tipos con minúscula y con mayúscula? Porque todos los declarados hasta ahora empiezan con minúscuala pero esto tiene mucha más pinta de Clase que de tipo como float32_y o bool_t.

Quizá difernciándolos con las mayúsculas pordíamos indicarle al usuario que estos se usan ligados a sus métodos y no se asignan directammente como los demás. De esta manera tipso como delay_t pasarían a ser Delay_t por ejemplo.

En cuanto a tus preguntas:

  • Full estatico: Preferiría que toda la biblioteca sea estática salvo casos donde realmente sea una gran ventaja que sea dinámica, que para el manejo de periféricos comunes no es necesario y ganamos mucha predictibilidad.
  • Igual de eficiente que las librerias <string.h> (requiere test para corroborar esto): Esto habría que verlo más al final.
  • Ocultar el uso de punteros: Seía muy bueno para el que arranca si tiene una UART y Strings sin usar punteros, se vuelve muy sencillo.
  • Compatible con los string NULL terminated?: Creo que estaría bueno para interfasear fácil con otras cosas.
  • Permitir strings sin null pointer termination: Tendría alguna ventaja además de ocupar un caracter menos en memoria?
  • Permitir substrings sin overhead de malloc: Se te ocurre como hacerlo?

¿String como un caso particular de un Stream genérico o como una cosa aparte?

@martinribelotta
Copy link
Contributor Author

@epernia 👍

En mi opinión:

  • Los tipos basicos son en minusculas y con _t final
  • Los tipos complejos son clases en si misma y usan CamelCase con _t final
  • Los objetos del sistema (puertos, ipcores, perifericos) deben ir en MAYUSCULAS (I2C0, SPI1, UART3)
  • Los objetos "dinamicos" (no necesariamente usando memoria dinamica) son CamelCase sin _t final

En cuanto a delay_t, si es un derivado de tipo basico, yo lo dejaria como delay_t. Si tiene algun mecanismo subyacente lo haría Delay_t pero solo si requiere el uso como clase.

Los string sin nullpointer pueden ser strings o substrings indistintamente.

Por ejemplo:

String_t s1 = StringNew("for I=1 to 10 step 2");
int token_count = StringSplit(s1, ' ', NULL, 0);
String_t tokens[token_count];
StringSplit(s1, ' ', tokens, ARRAY_LEN(tokens));

Es facil si usamos head y tail como dos punteros dentro de la struct.

Como vez, esa es la forma de tener substrings sin malloc. Basicamente, siempre estas haciendo referencia al mismo objeto, por lo tanto, tiene que haber alguna forma de hacer un string copy:

String_t s1, s2:

s1 = StringNew("Hola mundo");
StringBuffer_t buffer[StringLen(s1)];
s2 = StringCopy(s1, buffer);

@epernia
Copy link
Contributor

epernia commented Jan 6, 2017

@martinribelotta:

Entonces vamos con strings sin NULL si facilita las cosas.

Hay un tema que no me queda claro que es ¿Dónde pondríamos el vector con el string, dentro de la estructura o aparte?, lo tiene que crear el New o el Copy seguramente, allí se puede saber el largo en tiempo de compilación pero si luego queremos concatenar estamos fritos así que supongo que serán todos de un largo fijo, supongamos 256 caracteres, o ¿cómo lo pensaste esto?

@martinribelotta
Copy link
Contributor Author

@epernia

Los punteros del string apuntan a donde empieza y termina el string (posiblemente un tercer puntero para tomar el maximo del tail)

Esto es:

typedef struct {
  const char *head;
  const char *tail;
  const char *top;
} String_t;

String_t StringNewFromC(const char *cstr) {
  String_t v;
  v.head = cstr;
  v.tail = v.head + strlen(cstr);
  v.top = v.tail;
  return v;
}

String_t _StringNew(const char *ptr, size_t size) {
  String_t v;
  v.head = v.tail = ptr;
  v.top = ptr + size;
  return v;
}
#define StringNew(buf) _StringNew(buf, sizeof(buf))

En el caso de una concatenación, tenemos varias alternativas.

Manejarlo de forma directa pasandole el buffer

String_t s1, s2, s3;

s1 = StringNew("Hola ");
s2 = StringNew("Mundo");
char buffer[StringLen(s1) + StringLen(s2)];
s3 = StringConcat(s1, s2, buffer);

O tener un memory pool seleccionable o por defecto:

s3 = StringConcat(s1, s2, mpool);

Voy a seguir pensando alternativas, estaria bueno que alguien mas aporte

@epernia
Copy link
Contributor

epernia commented Jan 22, 2017

@martinribelotta:

Métodos de mínima que creo que deberíamos soportar para string:

  • uint32_t stringLen( String_t string ); o uint32_t stringSize( String_t string );: Devuelve la cantidad de caracteres.
  • String_t stringFirst( String_t string, uint32_t n ) o String_t stringLeft( String_t string, uint32_t n ): Retorna un nuevo String con n caracteres del string que recibe como parámetro, empezando por el principio de la cadena,
  • String_t stringLast( String_t string, uint32_t n ) o String_t stringRight( String_t string, int32_t n ): Retorna un nuevo String con los n caracteres del string que recibe como parámetro, empezando por el final de la cadena,
  • String_t stringMidN( String_t string, uint32_t n, uint32_t fromIndex ) y String_t stringMid( String_t string, uint32_t fromIndex, uint32_t toIndex ): Retorna un nuevo String con n caracteres del string que recibe como parámetro, empezando por fromIndex, o bien, desde fromIndex a toIndex.
  • String_t stringConcat( String_t string1, String_t string2 ): Retorna un nuevo String con el contenido de string1 seguido del contenido de string2.
  • String_t stringInsert( String_t string1, String_t string2, uint32_t fromIndex ): Retorna un nuevo String con el contenido de string 1 que agrega dentro el contenido de string2 a partir de la posición fromIndex.
  • String_t stringDelete( String_t string1, String_t string2, uint32_t fromIndex ): Retorna un nuevo String con el contenido de string 1 que agrega dentro el contenido de string2 a partir de la posición fromIndex.
  • String_t stringDeleteN( String_t string, uint32_t n, uint32_t fromIndex ) y String_t stringDelete( String_t string, int32_t fromIndex, int32_t toIndex ): Retorna un nuevo String con el el contenido del string que recibe como parámetro, borrándole n carecteres, empezando por fromIndex, o bien, borrando desde fromIndex a toIndex.
  • String_t stringReplaceN( String_t string1, String_t string2, uint32_t n, uint32_t fromIndex ) y String_t stringReplace( String_t string1, String_t string2, uint32_t fromIndex, uint32_t toIndex ): Retorna un nuevo String con el el contenido del string1 que recibe como parámetro, reemplazándole n carecteres e insertándole string2, empezando por fromIndex, o bien, borrando desde fromIndex a toIndex e insertandole string2. Es como hacer stringDelete() y luego stringInsert()
  • String_t stringReplaceN( String_t string1, String_t string2, uint32_t n, uint32_t fromIndex ) y int32_t stringFind( String_t string1, String_t string2 ): Busca string2 dentro de string1, retorna un entero con el índice de donde comienza string2 dentro de string1, o -1 en caso que no lo encuentre.

En caso de error en estos métodos habría que pensar una forma de marcarlo, quizá que devuelvan un NULL_STRING cuyos índices sean cosas sin sentido, o que todas devuelvan bool y el resultado lo ecriban en un string que les llega por referencia (me inclino por opcion 1).

@martinribelotta
Copy link
Contributor Author

martinribelotta commented Jan 23, 2017

@epernia

Teniendo en cuenta todo lo comentado, me inclino por esta estructura:

typedef struct {
  char *start;
  char *stop;
  size_t capacity;
} String_t;

Agrego algunos prototipos mas:

String_t stringNewFromPool(size_t capacity, Pool_t memoryPool/memoryHeap); //!< Crea un nuevo string pidiendo memoria a un pool
String_t stringNewFromHeap(size_t capacity, Heap_t memoryPool/memoryHeap); //!< Crea un nuevo string pidiendo memoria a un heap
const String_t stringNewFromCStr(const char *str); //!< Crea un nuevo string desde un C string. Notar que se retorna <const String_t>
String_t stringNewFromArray(char *ptr, size_t len); //!< Crea un nuevo string desde un buffer
String_t stringCopy(String_t to, const String_t from); //!< Copia `from` en `to` y devuelve `to`
bool stringIsEqual(const String_t a, const String_t b); //!< Retorna `true` si `a == b`
bool stringIsEqualNotCase(const String_t a, const String_t b); //!< Retorna `true` si `a == b` case insensitive
int stringDiff(const String_t a, const String_t b); //!< Retorna 0 si `a==b` o la diferencia del primer caracter distinto (util para implementar ordanamiento)

Aparte, en tus declaraciones falta distinguir entre const y no-const. Fijate que en mis prototipos queda claro que se modifica, que es constante y que no.

@epernia
Copy link
Contributor

epernia commented Jan 24, 2017

El codigo que charlamos por chat:

#define DECL_STRING(name, value)
char name##_strBuffer[sizeof(value)];
String_t name = StringNewFromC(value, name##_strBuffer)

DECL_STRING(a, "Hola mundo");
/*
char a_strBuffer[sizeof("Hola mundo")];
String_t a = StringNewFromC("Hola mundo", a_strBuffer)
*/

@epernia
Copy link
Contributor

epernia commented Jan 24, 2017

La biblioteca para usar como base:

http://www.and.org/ustr/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants