custom memstr (strstr) speed optimisation - c

I am writing a routine to find a string within a specified block of memory in an embedded (ARM Cortex M0 #16MHz) application and am wondering why the two different versions I have written run at different speeds.
char* memstr(char* mem, uint32_t n, char* str) {
if( (str[0] == '\0') || (n == 0) ) return NULL;
uint32_t i = 0;
char* max_mem;
max_mem = mem + n;
while( mem < max_mem ) {
if( *mem != str[i] ) {
mem -= i;
i = 0;
} else {
if(str[i+1] == '\0') return mem - i;
i++;
}
mem++;
}
return NULL;
}
char* memstr2(char* mem, uint32_t n, char* str) {
if( (str[0] == '\0') || (n == 0) ) return NULL;
uint32_t c = 0;
uint32_t i = 0;
while( c < n ) {
if( mem[c] != str[i] ) {
c -= i;
i = 0;
} else {
i++;
if(str[i] == '\0') return &mem[c - i + 1];
}
c++;
}
return NULL;
}
memstr is consistently a 1us faster than memstr2 when finding a 7 character string in between 20 and 200 bytes of memory. For example finding a 7 character string in 110 bytes, memstr takes 106us and memstr2 takes 107us. 1us may not sound a big deal but in an embedded application where every tick matters it's a drawback.
Kind of a bonus question: This also prompted me to write my own strstr which is faster than stock strstr (e.g. finding a 7 character string in a 207 character string takes my_strstr 236us and strstr 274us). What's wrong with this though as strstr must be pretty optimised?
char* my_strstr(char* str1, char* str2) {
uint32_t i = 0;
if( str2[0] == '\0' ) return NULL;
while( *str1 != '\0' ) {
if( *str1 != str2[i] ) {
str1 -= i;
i = 0;
} else {
i++;
if(str2[i] == '\0') return (str1 - i - 1);
}
str1++;
}
return NULL;
}

First, both functions don't work if you search for a string starting with two equal characters: If you search for xxabcde and the string contains xxxabcde then when you notice that that a of xxabcde doesn't match the third x, you already have skipped two x's and won't match the string.
You also don't check whether you search for an empty string, in which case your code produces undefined behaviour.
You compare memory with memory. But you can do an awful lot of work just comparing memory with a single character. If you search for "abcde", first you have to find the letter a. So I'd check for an empty string first, then read the first character. And then first loop to check for that character.
char first = str2 [0];
if (first == '\0') return mem;
for (; mem < maxmem; ++mem) if (*mem == first) {
... check whether there is a match
}
You should check your data. You would write different code if you expect the search string to come up early vs. if you expect it usually not to be there at all.

In memstr 'mem' is used as a pointer. In memstr2 'mem' is used as the name of an array 'mem[c]'. Depending on optimization a compiler might multiply by 1.
For instance in the statement:
if( mem[c] != str[i] ) {
mem[c] is calculated each time through the loop as
* ( &mem[0] + c * sizeof(mem[0]) )
A decent compiler would figure out that for a 'char' the sizeof(mem[0]) == 1 and the muliply could be skipped. If optimization were intentionally disabled as is typically done for debug versions I can imagine an extra multiply operation per loop. There would be a little extra calculation time in the memstr2 version even without the multiply, but it would surprise me if that were measurable.

It seems to me that the difference can be due to the fact that in the second version you dereference the pointer (return &mem[c - i - 1];) when you return from the function which can lead to access in memory which is costly and is something that can not happen in your first function (mem - i).
But the only way to be sure is to see what assembly is being created for each case.
I don't think this is about C but about compiler and platform

Related

void function that removes al the non alphabet chars

I am trying to write a program that gets several strings until it gets the 'Q' string (this string basically stops the scanf).
Each one of the strings is sent to a function that romoves everything except the letters. For example if I scan 'AJUYFEG78348' the printf should be 'AJUYFEG'.
The problem is that the function has to be void.
I have tried several ways to make the "new array with only letters" printed, but none of them worked.
(Is is not allowed to use strlen function)
#include <stdio.h>
void RemoveNonAlphaBetChars(char*);
int main()
{
int flag=1;
char array[100]={0};
while (flag == 1)
{
scanf("%s", &array);
if(array[0] == 'Q' && array[1] =='\0') {
flag=0;
}
while (flag == 1)
{
RemoveNonAlphaBetChars(array);
}
}
return 0;
}
void RemoveNonAlphaBetChars(char* str)
{
int i=0, j=0;
char new_string[100]={0};
for (i=0; i<100; i++)
{
if (((str[i] >= 'a') && (str[i] <= 'z')) || ((str[i] >= 'A') && (str[i] <= 'Z')))
{
new_string[j] = str[i];
j++;
}
}
printf("%s", new_string);
return;
}
The fact that the function has only one argument, non-const char pointer, hints at the fact that the string is going to be changed in the call (better document it anyway), and it's perfectly all right.
A few fixes to your code can make it right:
First, don't loop to the end of the buffer, just to the end of the string (without strlen, it's probably faster too):
for (i=0; str[i] != '\0'; i++)
then don't forget to nul-terminate the new string after your processing:
new_string[j] = '\0';
Then, in the end (where you're printing the string) copy the new string into the old string. Since it's smaller, there's no risk:
strcpy(str,new_string);
now str contains the new stripped string.
Another approach would be to work in-place (without another buffer): each time you encounter a character to remove, copy the rest of the string at this position, and repeat. It can be inefficient if there are a lot of characters to remove, but uses less memory.
The key here is that you are never inserting new characters into the string. That guarantees that the input buffer is large enough to hold the result. It also makes for an easy in-place solution, which is what the void return type is implying.
#include <ctype.h>
#include <stdio.h>
...
void RemoveNonAlphaBetChars(char* str)
{
char *from, *to;
for(from = to = str; *from; from++) {
if(isalpha(*from)) {
if(from > to) *to = *from;
to++;
}
}
*to = *from;
printf("%s\n", str);
return;
}
The pointer from steps along the string until it points to a NUL character, hence the simple condition in the loop. to only receives the value of from if it is a character. The final copy after the loop ensures NUL termination.
Update
If you are dealing with 1) particularly large strings, and 2) you have long stretches of letters with some numbers in between, and 3) your version of memmove is highly optimized compared to copying things manually (e.g. with a special processor instruction), you can do the following:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
...
void RemoveNonAlphaBetChars(char* str)
{
char *from, *to, *end;
size_t len;
for(from = to = str; *from; from = end) {
for(; *from && !isalpha(*from); from++) ;
for(end = from; *end && isalpha(*end); end++) ;
len = end - from;
if(from > to) {
if(len > 1) {
memmove(to, from, len);
} else {
*to = *from;
}
}
to += len;
}
*to = *end;
printf("%s\n", str);
return;
}
The general idea is to find the limits of each range of letters (between from and end), and copy into to block by block. As I stated before though, this version should not be used for the general case. It will only give you a boost when there is a huge amount of data that meets particular conditions.
void return type is a common approach to making functions that produce C string results. You have two approaches to designing your API:
Make a non-destructive API that takes output buffer and its length, or
Make an API that changes the the string in place.
The first approach would look like this:
void RemoveNonAlphaBetChars(const char* str, char *result, size_t resultSize) {
...
}
Use result in place of new_string, and make sure you do not go past resultSize. The call would look like this:
if (flag == 1) { // if (flag == 1), not while (flag == 1)
char result[100];
RemoveNonAlphaBetChars(array, result, 100);
printf("%s\n", result);
}
If you decide to use the second approach, move printf into main, and use strcpy to copy the content of new_string back into str:
strcpy(str, new_string);

Return a string made with a line read from input

i am trying to code a C function which returns a line read from the input as a char* . I am on Windows and i test my program in the command line by giving files as input and output of my program like this:
cl program.c
program < test_in.txt > test_out.txt
This is my (not working) function:
char* getLine(void)
{
char* result = "";
int i, c;
i = 1;
while((c = getchar()) != EOF)
{
*result++ = c;
i++;
if(c == '\n')
return result - i;
}
return result - i;
}
I was expecting it to work because i previously wrote:
char* getString(char* string)
{
//char* result = string; // the following code achieve this.
char* result = "";
int i;
for(i = 1; *result++ = *string++; i++);
return result - i;
}
And these lines of code have a correct behaviour.
Even if every answers will be appreciated, i would be really thankfull
if any of you could explain me why my getString() function works while my getLine() function doesn't.
Your function does not allocate enough space for the string being read. The variable char* result = "" defines a char pointer to a string literal ("", empty string), and you store some arbitrary number of characters into the location pointed to by result.
char* getLine(void)
{
char* result = ""; //you need space to store input
int i, c;
i = 1;
while((c = getchar()) != EOF)
{
*result++ = c; //you should check space
i++;
if(c == '\n')
return result - i; //you should null-terminate
}
return result - i; //you should null-terminate
}
You need to allocate space for your string, which is challenging because you don't know how much space you are going to need a priori. So you need to decide whether to limit how much you read (ala fgets), or dynamically reallocate space as you read more. Also, how to you indicate that you have finished input (reached EOF)?
The following alternative assumes dynamic reallocation is your chosen strategy.
char* getLine(void)
{
int ch; int size=100; size_t pos=0;
char* result = malloc(size*sizeof(char*));
while( (ch=getchar()) != EOF )
{
*result++ = ch;
if( ++pos >= size ) {
realloc(result,size+=100);
//or,realloc(result,size*=2);
if(!result) exit(1); //realloc failed
}
if( c=='\n' ) break;
}
*result = '\0'; //null-terminate
return result - pos;
}
When you are done with the string returned from the above function, please remember to free() the allocated space.
This alternative assumes you provide a buffer to store the string (and specifies the size of the buffer).
char* getLine(char* buffer, size_t size)
{
int ch;
char* result = buffer;
size_t pos=0;
while( (ch=getchar()) != EOF )
{
*result++ = ch;
if( ++pos >= size ) break; //full
if( c=='\n' ) break;
}
*result = '\0'; //null-terminate
return buffer;
}
Both avoid the subtle interaction between detecting EOF, and having enough space to store a character read. The solution is to buffer a character if you read and there is not enough room, and then inject that on a subsequent read. You will also need to null-ter
Both functions have undefined behaviour since you are modifying string literals. It just seems to work in one case. Basically, result needs to point to memory that can be legally accessed, which is not the case in either of the snippets.
On the same subject, you might find this useful: What Every C Programmer Should Know About Undefined Behavior.
Think of it this way.
When you say
char* result = "";
you are setting up a pointer 'result' to point to a 1-byte null terminated string (just the null). Since it is a local variable it will be allocated on the stack.
Then when you say
*result++ = c;
you are storing that value 'c' in to that address + 1.
So, where are you putting it?
Well, most stacks are to-down; so they grow toward lower addresses; so, you are probably writing over what is already on the stack (the return address for whatever called this, all the registers it needs restore and all sorts of important stuff).
That is why you have to be very careful with pointers.
When you expect to return a string from a function, you have two options (1) provide a string to the function with adequate space to hold the string (including the null-terminating character), or (2) dynamically allocate memory for the string within the function and return a pointer. Within your function you must also have a way to insure your are not writing beyond the end of the space available and you are leaving room for the null-terminating character. That requires passing a maximum size if you are providing the array to the function, and keeping count of the characters read.
Putting that together, you could do something similar to:
#include <stdio.h>
#define MAXC 256
char* getLine (char *s, int max)
{
int i = 0, c = 0;
char *p = s;
while (i + 1 < max && (c = getchar()) != '\n' && c != EOF) {
*p++ = c;
i++;
}
*p = 0;
return s;
}
int main (void) {
char buf[MAXC] = {0};
printf ("\ninput : ");
getLine (buf, MAXC);
printf ("output: %s\n\n", buf);
return 0;
}
Example/Output
$ ./bin/getLine
input : A quick brown fox jumps over the lazy dog.
output: A quick brown fox jumps over the lazy dog.

How to assign a value to a string outside the declaration in C ? Bus error/segfault

I'm trying to recode the equivalent of an strstr() function, after a few try I got it right, here the code:
(I know there are simpler way to do it which I did, but this time I wanted to try by using a 3rd string to stock the occurrence)
char *my_strstr(char *s1, char *s2)
{
int i, j;
char *tmp;
i = (j = 0);
if (s1 != '\0' && s2 != '\0')
{
while (s1[i] != '\0' && s2[j] != '\0')
{
if (s1[i] == s2[j])
{
tmp[j] = s1[i];
j++;
}
i++;
}
printf("tmp = %s\n", tmp);
}
return (tmp);
}
int main()
{
char a[] = "test Point123";
char b[] = "Point123";
char *ret;
ret = my_strstr(a, b);
printf("ret = %s\n",ret);
return (0);
}
I get the output I wanted:
tmp = Point123
ret = Point123
But then just to be sure I tried with a longer string, and that's where the problems started.
Here the string I tried,
char a[] = "test Point123456789";
char b[] = "Point123456789";
and the output I got with it:
tmp = Point123456?"1
ret = Point123456?"1
Abort trap: 6
With longer string I get sometimes segfault, sometimes bus Error 10.
On some other post I figured that the Bus error 10 sometimes replace a segfault on mac OS (on which I'm coding for the 1st time, I'm used to code on linux), I didnt find anything about the trap tho.
Anyway I figured its more a code problem that my compiler and I'd like to know why my code function on smaller string but not bigger ones, and I read that it could be how I affected value to the strings I'm using but I dont understand where I'm making an error.
So if anyone could give me a clue on what I'm doing wrong I'd greatly appreciate it :)
EDIT
I followed the advices in the comments, here is the function after I fixed it, it runs fine now thx.
(Again, I know I dont need a tmp and there are many other simpler and faster way to do it - it was meant as training (which apparently I needed ^^))
char *my_strstr(char *s1, char *s2)
{
int i, j;
char *tmp;
i = (j = 0);
tmp = malloc(sizeof(strlen(s2)));
if (s1 != '\0' && s2 != '\0')
{
while (s1[i] != '\0' && s2[j] != '\0')
{
if (s1[i] == s2[j])
{
tmp[j] = s1[i];
j++;
}
else
j = 0;
i++;
}
}
return (tmp);
}
In your code, tmp is an automatic local variable to your function my_strstr(). It is not initialized explicitly, so it contains indeterministic value.
Later in the code, you're directly writing
tmp[j] = s1[i];
Using (dereferencing) tmp uninitialized means you're trying to access invalid memory location (remember the "indeterministic value"?), which may not be accessible from your application. It invokes undefined behaviour.
Solution: You need to allocate memory to tmp before you access (dereference) it.
EDIT:
However, as per the below comment from Mr. Vlad, your logic for my_strstr() is also buggy. Once you're incrementing the j (index for the search string) and finding a mismatch in between, you're not re-setting it to 0. Maybe you want to have another look at it and correct the same.
You did not allocate memory for tmp so with tmp[j] you're writing to some random memory location which was not assigned to you. And this location may then be overwritten again by the things you do.
The longer the strings the more memory locations you're messing up.
You're just lucky that you got some results with shorter strings.

C : using strlen for string including \0

What I need to do is when given a text or string like
\0abc\n\0Def\n\0Heel\n\0Jijer\n\tlkjer
I need to sort this string using qsort and based on the rot encoding comparison.
int my_rot_conv(int c) {
if ('a' <= tolower(c) && tolower(c) <= 'z')
return tolower(c)+13 <= 'z' ? c+13 : c-13;
return c;
}
int my_rot_comparison(const void *a, const void *b) {
char* ia = (char*) a;
char* ib = (char*) b;
int i=0;
ia++, ib++;
while (i<strlen(ia)) {
if (ia[i] == '\0' || ia[i] == '\n' || ia[i] == '\t' || ib[i] == '\0' || ib[i] == '\n' || ib[i] == '\t') {
i++;
}
if (my_rot_conv(ia[i]) > my_rot_conv(ib[i])) {
return 1;
} else if (my_rot_conv(ia[i]) < my_rot_conv(ib[i]))
return -1;
}
return 0;
}
I get to the point that I compare two string that starts with \0, getting the -1 in the following example.
printf("%d \n", my_rot_comparison("\0Abbsdf\n", "\0Csdf\n"));
But this wouldn't work for a string with qsort because ia++, ib++; does work only for one word comparison.
char *my_arr;
my_arr = malloc(sizeof(\0abc\n\0Def\n\0Heel\n\0Jijer\n\tlkjer));
strcpy(my_arr, \0abc\n\0Def\n\0Heel\n\0Jijer\n\tlkjer);
qsort(my_arr, sizeof(my_arr), sizeof(char), my_rot_comparison);
and the array should be sorted like \0Def\n\0Heel\n\0Jijer\n\0\n\tlkjer
My question is how do I define the comparison function that works for the string that includes \0 and \t and \n characters?
strlen simply cannot operate properly on a string which embeds \0 bytes, since by definition of the function strlen considers the end of the string to be the first encountered \0 byte at or after the beginning of the string.
The rest of the standard C string functions are defined in the same way.
This means that you have to use a different set of functions to manipulate string(-like) data that can include \0 bytes. You will perhaps have to write these functions yourself.
Note that you will probably have to define a structure which has a length member in it, since you won't be able to rely on a particular sentinel byte (such as \0) to mark the end of the string. For example:
typedef struct {
unsigned int length;
char bytes[];
}
MyString;
If there is some other byte (other than \0) which is forbidden in your input strings, then (per commenter #Sinn) you can swap it and \0, and then use normal C string functions. However, it is not clear whether this would work for you.
assuming you use an extra \0 at the end to terminate
int strlenzz(char*s)
{
int length =0;
while(!(*s==0 && *(s+1) == 0))
{
s++;
length++;
}
return length+1
}
Personally I'd prefer something like danfuzz's suggestion, but for the sake of listing an alternative...
You could use an escaping convention, writing functions to:
"escape" / encode, expanding embedded (but not the terminating) '\0'/NUL to say '\' and '0' (adopting the convention used when writing C source code string literals), and
another to unescape.
That way you can still pass them around as C strings, your qsort/rot comparison code above will work as is, but you should be very conscious that strlen(escaped_value) will return the number of bytes in the escaped representation, which won't equal the number of bytes in the unescaped value when that value embeds NULs.
For example, something like:
void unescape(char* p)
{
char* escaped_p = p;
for ( ; *escaped_p; ++escaped_p)
{
if (*escaped_p == '\\')
if (*++escaped_p == '0')
{
*p++ = '\0';
continue;
}
*p++ = *escaped_p;
}
*escaped_p = '\0'; // terminate
}
Escaping is trickier, as you need some way to ensure you have enough memory in the buffer, or to malloc a new buffer - either of the logical size of the unescaped_value * 2 + 1 length as an easy-to-calculate worst-case size, or by counting the NULs needing escaping and sizing tightly to logical-size + #NULs + 1....

strlen inconsistent with zero length string

I'm creating a DataStage parallel routine, which is a C or C++ function that is called from within IBM (formerly Ascential) DataStage. It is failing if one of the strings passed in is zero length. If I put this at the very first line of the function:
return strlen(str);
then it returns 0 for the calls that pass in empty values into str. If I put this at the first line, however...
if (strlen(str)==0) {return 0;}
then it does not return and goes into an infinite loop
I'm baffled - it works fine in a test harness, but not in DataStage.
Maybe there is something odd about the way DataStage passes empty strings to C routines?
int pxStrFirstCharList(char *str, char *chars )
{
if (strlen(str)==0) {return 0;}
if (strlen(chars)==0) {return 0;}
int i = 0;
//Start search
while (str[i]) //for the complete input string
{
if (strchr(chars, str[i]))
{
return i+1;
}
++i;
}
return 0;
}
There is a builtin function for what you are doing, it's called strcspn. This function takes two strings, and searches the first one for the first occurance of any of the characters of the second string.
I suggest using that than RYO...
http://www.cplusplus.com/reference/clibrary/cstring/strcspn/
How about this?
int pxStrFirstCharList(char *str, char *chars )
{
if (str && chars && (0 != strlen(str)) && (0 != strlen(chars)))
{
int i = 0;
//Start search
while (str[i]) //for the complete input string
{
if (strchr(chars, str[i]))
{
return i+1;
}
++i;
}
}
return 0;
}
Also, I don't quite get the point of the while loop ... (and no, I don't mean that this could be written as for). What I mean is that on one hand you are doing a search (strstr) that itself will be implemented as a loop and still you have some outer loop. Could it be that you actually wanted to have chars in its place, i.e.:
int pxStrFirstCharList(char *str, char *chars )
{
if (str && chars && (0 != strlen(str)) && (0 != strlen(chars)))
{
int i = 0;
//Start search
while (chars[i]) //for the complete input string
{
if (strchr(str, chars[i]))
{
return i+1;
}
++i;
}
}
return 0;
}
...? That is, look for each of the characters within chars inside the string denoted by str ...
If NULL is not explicitly part of the game, at least during development phase, it's always a good idea to add a precondition check on pointers received by a function:
int pxStrFirstCharList(char *str, char *chars )
{
if (!str)
return -1;
if (!chars)
return -2;
....
(The negative values -1 and -2 than tell the caller that something went wrong)
Or doing it in a more relaxed way, silently accepting NULL pointer strings as ""-string:
int pxStrFirstCharList(char *str, char *chars )
{
if (!str)
return 0;
if (!chars)
return 0;
...
If you are the only one using this API you could #ifndef BUILD_RELEASE these checks away for a release build if anything is tested stable.
I guess it is the strlen's issue when the length of the string is 0. For example,
char s1[0];
char *s2="a";
printf("%d %s\n", sizeof(s1), s1);//0 #
printf("%d %s\n", strlen(s1), s1);//3 #
printf("%d %s\n", sizeof(s2), s2);//8 a
printf("%d %s\n", strlen(s2), s2);// 1 a
You will get a weird answer for using strlen and you can check its source code in detail(https://code.woboq.org/userspace/glibc/string/strlen.c.html). In nutshell, you can use sizeof instead of strlen for char string or avoid 0 length case by using strlen.

Resources