How can i speed up hexadecimal characters conversion to binary characters - c

I have already a function that convert hex char(input) to binary char(output). it works perfect, for small amount of data(input length).But when the input is too big, it stuck/not working. May be strcat take too much time. Is there some alternate solution, So i can convert big hex input characters into equivalent binary.
My function is:
void fun_hex_ch_2bin(int len_hex_str,uint8_t *hex,uint8_t *bin){
/* Extract first digit and find binary of each hex digit */
int i=0,j=0;
char array_hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
uint8_t *new_hex=malloc(len_hex_str*2);
char hex_char1,hex_char2;
j=0;
for(i=0;i<len_hex_str;i++)
{
hex_char1=array_hex[hex[i]&0x0f];
hex_char2=array_hex[(hex[i]>>4)&0x0f];
//printf("%c %c\n",hex_char1,hex_char2);
new_hex[j]=hex_char2;
new_hex[j+1]=hex_char1;
j=j+2;
}
for(i=0; i<len_hex_str*2; i++)
{
switch(new_hex[i])
{
case '0':
strcat(bin, "0000");
break;
case '1':
strcat(bin, "0001");
break;
case '2':
strcat(bin, "0010");
break;
case '3':
strcat(bin, "0011");
break;
case '4':
strcat(bin, "0100");
break;
case '5':
strcat(bin, "0101");
break;
case '6':
strcat(bin, "0110");
break;
case '7':
strcat(bin, "0111");
break;
case '8':
strcat(bin, "1000");
break;
case '9':
strcat(bin, "1001");
break;
case 'a':
case 'A':
strcat(bin, "1010");
break;
case 'b':
case 'B':
strcat(bin, "1011");
break;
case 'c':
case 'C':
strcat(bin, "1100");
break;
case 'd':
case 'D':
strcat(bin, "1101");
break;
case 'e':
case 'E':
strcat(bin, "1110");
break;
case 'f':
case 'F':
strcat(bin, "1111");
break;
default:
printf("Invalid hexadecimal input.");
}
}
}

Just use sprintf() instead of strcat()
char *bin; // points to a long enough buffer
int binlen = 0;
binlen += sprintf(bin + binlen, "something"); // strcat(bin, "something");
binlen += sprintf(bin + binlen, "otherthing"); // strcat(bin, "otherthing");
binlen += sprintf(bin + binlen, "foobar"); // strcat(bin, "foobar");
//...
// you can even do
binlen += sprintf(bin + binlen, "%.2f", 2.71828); // strcat(bin, "2.72");

16 is a power of 2 so converting it to binary is pretty simple.
Each hex digit corresponds to exactly 4 binary digits - and you can rely on this fact.
As a first step you need to convert the input characters from ASCII to numeric values.
That's easily done in one pass over the input hex string and subtracting 48 if the character is between '0' and '9' or subtracting 88 if the character is between 'a' and 'f' (take a look at the ASCII table if need an explanation on why).
After that the conversion is straightforward - go over the hex array and for each hex value take a look at the last bit and proceed to the next bit exactly 4 times, move to the next hex value and repeat the procedure.
Something like this:
int i = 0, j = 0;
while(i < len_hex_str)
{
bin[j++]=hex[i] & 1;
hex[i] >>= 1;
if(j % 4 == 0) i++;
}
And since you seem to be in need to have it ASCII representation, just pass over the output string and add 48 to each digit.

How can i speed up hexadecimal characters conversion to binary characters (?)
May be strcat take too much time.
Yes. Each call to strcat() takes longer and longer time as code does not take advantage of data already converted.
strcat() take n time to traverse the first characters.
1st strcat call, n = 0
2st strcat call, n = 8
3rd strcat call, n = 16
4th strcat call, n = 24
ith strcat call, n = 8*(i-1)
See how the sum (0+8+16+24+...) goes up by order of i*i as i increases?
Note that the first call to strcat(bin, ...) is suspect as bin[0] is not certainly a null character - something required when concatenating to a string.
Is there some alternate solution (?)
I recommend a re-write. Directly read from hex as binary and skip the in-between conversion to hexadecimal.
void fun_hex_ch_2bin(int len_hex_str, uint8_t *hex, uint8_t *bin) {
while (len_hex_str > 0) {
len_hex_str--;
// Start with the MSBit
for (uint8_t mask = 0x80; mask; mask >>=1) {
*bin++ = mask & *hex ? '1' : '0';
}
hex++;
}
// Append a null character as `bin` is to point to a _string_.
*bin = '\0';
}
I'd expect bin, as a string to be char* and not unit8_t *.

Related

How to get rid of the Heap-corruption error (Critical error c0000374) in C when converting hex into binary string?

Part of my project, where we have to take an input file with hex numbers and convert them to MIPS code, I want to convert the hex into binary so it'd be easier for me to convert it into MIPS. However, when I run the code, it crashes and quits when it reaches the part where it calls the converter function. GDB says its a critical error c0000374. How do I fix this?
I have tried giving the target string more space and it doesn't seem to have any effect. I have also tried using malloc to no avail.
char* convertBinary (int hex)
{
char* hexdec = calloc(9, sizeof(char));
char* bin = calloc(SIZE+1, sizeof(char));
snprintf(hexdec, SIZE, "%08X", hex);
long int i;
for (i = 0; hexdec[i]; ++i)
{
switch (hexdec[i])
{
case '0':
strcat(bin, "0000");
break;
case '1':
strcat(bin, "0001");
break;
case '2':
strcat(bin, "0010");
break;
case '3':
strcat(bin, "0011");
break;
case '4':
strcat(bin, "0100");
break;
case '5':
strcat(bin, "0101");
break;
case '6':
strcat(bin, "0110");
break;
case '7':
strcat(bin, "0111");
break;
case '8':
strcat(bin, "1000");
break;
case '9':
strcat(bin, "1001");
break;
case 'A':
case 'a':
strcat(bin, "1010");
break;
case 'B':
case 'b':
strcat(bin, "1011");
break;
case 'C':
case 'c':
strcat(bin, "1100");
break;
case 'D':
case 'd':
strcat(bin, "1101");
break;
case 'E':
case 'e':
strcat(bin, "1110");
break;
case 'F':
case 'f':
strcat(bin, "1111");
break;
default:
printf("\nInvalid hexadecimal digit %c",
hexdec[i]);
}
}
return bin;
}
Also, in case it helps, here is the main function where I call this function
int main ()
{
int command = 10010100; //This is in hex
char* binaryString = convertBinary(command);
printf("The coverted binary is: %s\n", binaryString);
}
I expect the function to return a string of the binary numbers that have been converted from an 8 digit hex number. However, the program just quits and doesn't output anything. When debugged with GDB, it lays out a warning saying,
warning: Critical error detected c0000374
There are multiple problems in your code:
You do not check the for memory allocation failure.
Since you allocate 9 bytes for hexdec, snprintf(hexdec, SIZE, "%08X", hex); should be
snprintf(hexdec, 9, "%08X", hex);
The definition of SIZE is missing, as well as the #include lines. Post the complete source of the program exhibiting the offending behavior.
There is no need to loop until the end of the string hexdec: since you convert the hex value with %08X, just loop with:
for (i = 0; i < 8; ++i)
You should free(hexdec) before leaving the convertBinary function.
The code and comment do not agree in int command = 10010100; //This is in hex, which one is wrong? Probably both.
There is no need to use long type for i, int will suffice. Conversely, the argument hex should have unsigned int type.
Here is a simplified version of your code:
#include <stdio.h>
#include <stdlib.h>
char *convertBinary(unsigned int hex) {
char *bin = calloc(33, 1);
int i;
if (bin) {
for (i = 32; i-- > 0;) {
bin[i] = '0' + (hex & 1);
hex >>= 1;
}
}
return bin;
}
int main() {
int command = 0x10010100; //This is in hex
char *binaryString = convertBinary(command);
if (binaryString == NULL) {
printf("Memory allocation failure\n");
} else {
printf("The converted binary is: %s\n", binaryString);
free(binaryString);
}
return 0;
}

Assigning a string in a char * array to another char * array

I am trying to convert the hex values in array a to binary values and assign converted values to array b then print the array b. But all values in array b are same. The output is:
111100001011000100010111101010001101
111100001011000100010111101010001101
111100001011000100010111101010001101
If I use b[i] = strdup(hexToBin(a[i])); instead of b[i] = hexToBin(a[i]); the output will be:
111100001011
111100001011000100010111
111100001011000100010111101010001101
Is this something about pointers? Char * is a pointer which points first character of the string and are all characters after the pointer printed? What is right way of doing this?
#include <stdio.h>
#include <string.h>
char bin[100] = "";
char * hexToBin(char hex[50]);
int main(void) {
char * a[] = {
"f0b",
"117",
"a8d",
};
char * b[3];
for(int i = 0; i < 3; i++) {
b[i] = hexToBin(a[i]);
}
for(int i = 0; i < 3; i++) {
printf("%s\n", b[i]);
}
}
char * hexToBin(char hex[50]) {
for(int i=0; hex[i]!='\0'; i++)
{
switch(hex[i])
{
case '0':
strcat(bin, "0000");
break;
case '1':
strcat(bin, "0001");
break;
case '2':
strcat(bin, "0010");
break;
case '3':
strcat(bin, "0011");
break;
case '4':
strcat(bin, "0100");
break;
case '5':
strcat(bin, "0101");
break;
case '6':
strcat(bin, "0110");
break;
case '7':
strcat(bin, "0111");
break;
case '8':
strcat(bin, "1000");
break;
case '9':
strcat(bin, "1001");
break;
case 'a':
case 'A':
strcat(bin, "1010");
break;
case 'b':
case 'B':
strcat(bin, "1011");
break;
case 'c':
case 'C':
strcat(bin, "1100");
break;
case 'd':
case 'D':
strcat(bin, "1101");
break;
case 'e':
case 'E':
strcat(bin, "1110");
break;
case 'f':
case 'F':
strcat(bin, "1111");
break;
default:
printf("Invalid hexadecimal input.");
}
}
return bin;
}
The hexToBin function returns a pointer to the first element of the global bin array. Everytime!.
That means all pointers in b will be the very same pointer to the very same first element of the bin array.
If you know the maximum length of the strings, I recommend that you make b an array of arrays of char. For example
char b[3][500]; // 3 arrays of 499-character strings (+1 for the null-terminator)
Then instead of hexToBin returning a pointer to a single global array, pass a pointer to the string to be filled as argument to hexToBin:
void hexToBin(char *hex, char *bin);
and call it as
hexToBin(a[i], b[i]);
You only have one bin. What your hexToBin does is appending to that one bin and then returning that bin. In other words, when you call it multiple times, the result is always the same pointer, because you always return bin;.
So if you do this:
b[i] = hexToBin(a[i]);
Then in the end, all elements of b are pointing to bin, that's why you get the same output when you print them. If you do this instead:
b[i] = strdup(hexToBin(a[i]));
Then the result is not the same, because they all don't get bin assigned, but a copy of what bin has been at that time. That's why the results are different. So b[0] points to one copy, then bin is appended to again, but that doesn't change the b[0] copy.
If you use strdup, don't forget to free the memory it allocated:
for(int i = 0; i < 3; i++) {
free(b[i]);
}
[My answer is mistaken. I leave it posted here for reference but the other answers are preferable.]
Your code looks pretty good for beginner's code and I like your style. I especially like this line:
char * b[3];
Unfortunately, for this particular application, you must replace this with a less elegant line like
char b[3][5];
The former line reserves no storage for your output. The latter line reserves five bytes per hex digit. You need storage, somehow.

how to fill an array with 0's in C

For my project i need to make an hexadecimal calculator and additional to that i need to convert it to binary and show it, witch i know is easy. The max of hexadecimal i can read is '8' but if i read a hexadecimal with less than 8 characters i need to fulfill the first binary numbers with 0. Ej:
in Hex:ADA0CAFE
in Binary:10101101 10100000 11001010 11111110
but if i get an hex like:C45FA
the output should be: 00000000 00001100 01000101 11111010
Part of my code look something like this:
char hex[100];
printf("Hexadecimal: ");
scanf("%s",hex);
convert(hex);
and the function to convert it:
void convert(char hex[]){
for(i=0;i<8;i++){
switch(hex[i]){
case '0': printf("0000"); break;
case '1': printf("0001"); break;
case '2': printf("0010"); break;
case '3': printf("0011"); break;
.
.
.
}
So the part i dont figure out how to is to get the first position of the array fill with 0 and then start writing the array with the character of the hexadecimal.
In convert find the length of the hex string passed, and put out leading 0000 strings to make up the length to 8. Perhaps like this:
void convert(char hex[]){
size_t len, i;
len = strlen(hex);
for(i = len; i < 8; i++) {
printf("0000"); // pad with leading zeros
}
for(i = 0; i < len; i++) { // the rest of the string
switch(hex[i]) {
case '0': printf("0000"); break;
case '1': printf("0001"); break;
case '2': printf("0010"); break;
case '3': printf("0011"); break;
.
.
.
}
}
}

C lexical analyzer. Using switch to analyze and count a decimal/not decimal

My lexical analyzer recognizes digits(5,555,543667), decimals(44.65,4.1), and periods(.).
I can count digits, decimals, and periods fine but when I come across a digit and period next to each other it counts it as a decimal.
Consider a text file that contains: 555 2.3 55.23 44 5.
My output would be
1 type 1: 555
2 type 3: 2.3
3 type 3: 55.23
4 type 1: 44
5 type 3: 5.
Where type 3 is my identifier for a decimal.
I would want the 5th and 6th tokens to be counted as a digit and then a period.
Here is how I am handling my switch statement.
switch(*b) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
digits:
t.length++;
switch(*(b + t.length)) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
goto digits;
case '.':
goto decimal;
break;
default:
break;
}
t.type = TOKEN_DIGITS;
t.string = (char *)calloc(t.length + 1, sizeof(char));
strncpy(t.string, b, t.length);
break;
decimal:
t.length++;
switch(*(b + t.length)) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
goto decimal;
break;
}
t.type = TOKEN_DECIMAL;
t.string = (char *)calloc(t.length+1,sizeof(char));
strncpy(t.string,b,t.length);
break;
Tried multiple things but I am officially stuck.
You really should be using character classification functions for this kind of excercise instead of long switch statements. Your code will be a lot simpler and you won't have to use goto at all.
For example, a number could be described with the following regular expression (added whitespace to break up the various blocks):
[-+]? [0-9]* \.? [0-9]+
This already shows the possible state transitions:
A number can (optionally) start with + or - (if you support signed numbers)
It may have 0..n digits
If the following character is not a decimal point symbol, it should be a separator, otherwise it's an invalid symbol. If it's a separator, your number is terminated.
After a decimal point there should be 1..n digits
The number is terminated when you reach the end of input or you encounter a separator
All this can be done in a handful of lines of code - just have a pointer that points to your current input character and then keep stepping forward one by one and examine each character and based on the character class, decide what to do.
Now, this particular approach doesn't handle floating point numbers using scientific notation, etc. but adding thos extras is really simple once you have the basics done.
I think this complements xxbbcc's answer.
*Very roughly * something like this.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
yylex() {
int c;
char *p, buf[1000];
for(c = get(); isspace(c); c = get());
if(isdigit(c)) {
p = buf;
while(isdigit(c)) {
*p++ = c;
c = get();
}
*p = 0;
if(c != '.') {
unget(c);
int i = atoi(buf);
return INT;
}
assert(c == '.');
*p++ = c;
c = get();
while(isdigit(c)) {
*p++ = c;
c = get();
}
*p = 0;
float f = atof(buf);
unget(c);
return DECIMAL;
}
}
There's a lot of details left unsaid. Watching for EOF. Buffer overflow. Setting yylval to the int or float. Parsing tokens other than simple numbers.
Use a variable like digit_follow_peroid to keep the state. Every time you encounter a peroid, set the variable to false and then when you encounter a digit in the decimal switch block, set it to true. check the variable's value to determine the t.lengthbefore strncpy. Maybe you also need other variables to work together with it. The best way is to define a state-transition matrix, which is much better than gotos.

Why is "\b" not clearing one character at left?

I'm trying to print 01-99 in words and I am somewhat successful.
Here is the source code:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char a, b;
char *digit1;
char *digit2;
// get digit character by character
scanf("%c%c", &a, &b);
switch(a) {
case '1':
switch(b) {
case '0':
digit1 = "ten";
break;
case '1':
digit1 = "eleven";
break;
case '2':
digit1 = "twelve";
break;
case '3':
digit1 = "thirteen";
break;
case '4':
digit1 = "fourteen";
break;
case '5':
digit1 = "fifteen";
break;
case '6':
digit1 = "sixteen";
break;
case '7':
digit1 = "seventeen";
break;
case '8':
digit1 = "eighteen";
break;
case '9':
digit1 = "nineteen";
break;
default:
digit1 = "";
break;
}
break;
case '2':
digit1 = "twenty-";
break;
case '3':
digit1 = "thirty-";
break;
case '4':
digit1 = "forty-";
break;
case '5':
digit1 = "fifty-";
break;
case '6':
digit1 = "sixty-";
break;
case '7':
digit1 = "seventy-";
break;
case '8':
digit1 = "eighty-";
break;
case '9':
digit1 = "ninty-";
break;
default:
digit1 = "";
}
switch(b) {
case '1':
digit2 = "one";
break;
case '2':
digit2 = "two";
break;
case '3':
digit2 = "three";
break;
case '4':
digit2 = "four";
break;
case '5':
digit2 = "five";
break;
case '6':
digit2 = "six";
break;
case '7':
digit2 = "seven";
break;
case '8':
digit2 = "eight";
break;
case '9':
digit2 = "nine";
break;
case '0':
digit2 = "\b";
break;
default:
digit2 = strcpy(digit1, "\b");
}
if (a != 1) {
printf("%s%s\n", digit1, digit2);
}
else {
printf("%s\n", digit1);
}
return 0;
}
I'm successful in printing from 20-99 until now. But there is an error. If I enter any of 20, 30, 40... - is not removed as it should be cause I used \b to remove that.
You could replace
digit2 = "\b";
by
if (digit1[0] != '\0')
digit1[strlen(digit1) - 1] = '\0';
which effectively removes the trailing character from digit1.
Since this works completely on the level of your strings, you avoid relying on device-specific behaviour as \b would do.
This is a little outside of the C standard as it concerns the behavior of the input/output environment, but usually the backspace character just repositions the cursor. To back up and erase, try the sequence BACKSPACE SPACE BACKSPACE.
This is the behavior of the DEC VT100 terminal, which is pretty ubiquitous (also called ANSI terminal, same is used by XTERM-derived terminals). As #jxh points out, adding terminal-specific sequences is only appropriate if the output device is a terminal. If you're writing to a file, this will not erase, but simply add these additional bytes to the output stream. However, these delayed sequences would still be interpreted correctly if written to the terminal at a later time, perhaps using cat. You could use the POSIX function isatty(3) to get a good guess at whether these sequences will be interpreted.
It may be possible to unwrite a character in a buffered write by adjusting the file position.
FILE *myfile = /*initialization*/;
/* ... */
fsetpos (myfile, -1, SEEK_CUR);
Please also consider MvG's valuable comments for more ways these methods may fail. (It really is shaky territory, being outside of the standard. brrrrr. :)
The escape code "\b" emits a terminal-control character; that is, it writes an additional control character to the output stream, it does not remove previous characters from the output stream: On most systems "hellO\bo" produces a string literal of 8 bytes equivalent to { 'h', 'e', 'l', 'l', 'O', '\b', 'o', '\0' };
It is important, though, to remember that this is a terminal-control character. What it does is terminal dependent: some terminals move the cursor left stopping at column 0, some move the cursor left with wrap, some erase the character the cursor arrives at, others don't.
But further more, if you are viewing the data other than through a terminal, it is just a byte. E.g. the code
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("Hello!\b\n");
}
will generate a sequence of ascii values that, if viewed with linux "cat" might display "Hello", with Windows/DOS "more" command, "Hello!" (DOS is a non-destructive backspace) and if loaded in notepad produce "Hello!" followed by what looks like a wing-ding.
Don't confuse terminal control with io-stream control.

Resources